Уточните открытый / закрытый принцип

25

Как я уже объяснил, принцип открытия / закрытия гласит, что однажды написанный код не должен изменяться (кроме исправлений ошибок). Но если мои бизнес-правила меняются, не должен ли я изменить код, реализующий эти изменения? Я подозреваю, что я не понимаю, как принцип, потому что это не имеет смысла для меня.

Уинстон Эверт
источник

Ответы:

22

Это, наверное, самый сложный из твердых принципов, чтобы объяснить. Дай мне попробовать. Представьте, что вы написали класс Invoice, который отлично работает и не содержит ошибок. Это делает PDF счета-фактуры.

Затем кто-то говорит, что хочет получить HTML-счет со ссылками. Вы не изменяете какой-либо код в инвойсе для удовлетворения этого запроса. Вместо этого вы создаете другой класс, HTMLInvoice, который делает то, что они сейчас хотят. Вы используете наследование, чтобы вам не пришлось писать много повторяющегося кода в HTMLInvoice.

Старый код, который использовал старую Invoice, не поврежден и не затронут. Новый код может использовать HTMLInvoice. (Если вы также делаете лисковскую заменяемость , L твердого тела, вы можете передать экземпляры HTMLInvoice существующему коду, ожидающему экземпляры Invoice.) Все живут долго и счастливо.

Счет закрыт для модификации, открыт для расширения. И вы должны написать счет заранее, чтобы это работало, кстати.

Кейт Грегори
источник
1
Если бизнес-правила меняются, нет предположения о том, что они работают без ошибок, поэтому принцип открытия / закрытия не применим?
JeffO
Я боролся с этим правилом сам, и то, что Кейт предлагает, в основном то, что я пришел к нему. В бизнесе вы пытаетесь запрограммировать меньшие, более гибкие классы, чтобы вы могли хорошо их использовать. Если они работают правильно, вы не хотите изменять их. Но они редко полностью «делаются», поэтому некоторые изменения неизбежны. Обратите внимание, что текст говорит «модуль», а не объект. Я часто успешно применяю OCP на функциональном уровне с помощью ограниченных функций, которые отлично выполняют одну вещь и никогда не нуждаются в изменении.
CodexArcanum
1
@Jeff O. Я делаю различие между исправлением ошибки (когда код не соответствовал исходному требованию, и никто не хочет его таким, как есть) и изменением требований. Если мне требуются PDF-файлы, а код создает PDF-файлы, то здесь нет ошибки, хотя я теперь хочу HTML (и обычно люди тоже хотят HTML, а не вместо этого)
Кейт Грегори
2
@Winston - это то, что я имел в виду, когда сказал, что вы должны правильно составить счет-фактуру. В идеале уже был довольно абстрактный счет, и вы унаследовали PDFInvoice, ожидая этого. Если нет, вам нужно один раз нарушить правило, чтобы настроить себя так, чтобы не нарушать его в будущем. В любом случае, предсказание будущих изменений - огромная часть всего этого - и это часть рецепта «поймай и порежь слона».
Кейт Грегори
1
Ваше последнее утверждение является наиболее важным. Открытое / закрытое - это идеал дизайна, и чтобы его достичь, вам нужно сразу разработать дизайн. Не все должно удовлетворять открытию / закрытию, но это мощный инструмент, если вы можете туда добраться.
Алекс Фейнман,
13

Вы читали статью « Принцип открытых дверей» от приятелей дяди Боба в ObjectMentor? Я думаю, что это одно из лучших объяснений.

Есть много эвристик, связанных с объектно-ориентированным дизайном. Например, «все переменные-члены должны быть частными», или «следует избегать глобальных переменных», или «использование идентификации типа во время выполнения (RTTI) опасно»). Каков источник этой эвристики? Что делает их правдой? Они всегда правдивы? В этой колонке исследуется принцип проектирования, лежащий в основе этой эвристики - принцип открытого-закрытого типа.

Как сказал Ивар Якобсон: «Все системы меняются в течение своих жизненных циклов. Это необходимо учитывать при разработке систем, которые, как ожидается, будут работать дольше, чем первая версия ». Как мы можем создавать проекты, которые стабильны перед лицом изменений и будут работать дольше, чем первая версия? Бертран Мейер дал нам руководство еще в 1988 году, когда он придумал известный теперь принцип открытого-закрытого типа. Перефразируя его:

ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ (КЛАССЫ, МОДУЛИ, ФУНКЦИИ, И Т.Д.) ДОЛЖНЫ БЫТЬ ОТКРЫТЫ ДЛЯ РАСШИРЕНИЯ, НО ЗАКРЫТЫ ДЛЯ МОДИФИКАЦИИ.

Когда одно изменение в программе приводит к каскаду изменений в зависимых модулях, эта программа демонстрирует нежелательные атрибуты, которые мы привыкли ассоциировать с «плохим» дизайном. Программа становится хрупкой, жесткой, непредсказуемой и не подлежащей повторному использованию. Принцип «открыто-закрыто» атакует это очень простым способом. Это говорит о том, что вы должны разрабатывать модули, которые никогда не меняются . Когда требования меняются, вы расширяете поведение таких модулей, добавляя новый код, а не изменяя старый код, который уже работает.

Описание

Модули, которые соответствуют принципу открытого-закрытого, имеют два основных атрибута.

  1. Они «открыты для расширения».
    Это означает, что поведение модуля может быть расширено. То, что мы можем заставить модуль вести себя по-новому и по-разному при изменении требований приложения или для удовлетворения потребностей новых приложений.
  2. Они «закрыты для модификации».
    Исходный код такого модуля нерушим. Никто не может вносить изменения в исходный код.

Казалось бы, эти два атрибута расходятся друг с другом. Обычный способ расширить поведение модуля - внести изменения в этот модуль. Обычно считается, что модуль, который нельзя изменить, имеет фиксированное поведение. Как можно разрешить эти два противоположных атрибута?

Абстракция - это ключ ...

Мартейн Вербург
источник
3
Это хорошая статья, объясняющая абстракцию. Тем не менее, есть фундаментальный момент, который следует учитывать, и это был хороший абстрактный дизайн, изложенный в первую очередь? Многие магазины имеют много устаревшего кода, и единственный способ изменить его - это «модификация», а не «расширение». Если это так, то, вероятно, нужно работать, чтобы изменить это, но пока это не произойдет, вы застряли в модификации кода.
Майкл К
@ Крис, круто - я также рекомендую книгу "Чистый код" дяди Боба, если тебе нравятся подобные вещи.
Мартейн Вербург
@ Майкл - Полностью согласен, это почти как необходимость рефакторинга кода, чтобы сделать его идеальным для тестирования.
Мартейн Вербург
Статья демонстрирует важность абстракции очень красиво. Но я не понимаю связи между абстракциями и не пытаюсь никогда не изменять модули после того, как я их напишу. Абстракция означает, что я могу модифицировать модуль X без внесения изменений в модуль Y. Но разве нет смысла делать это, поэтому я могу модифицировать модуль X или модуль Y, если мне это нужно?
Уинстон Эверт
1
Вау. Код нерушим? Я никогда не был большим поклонником дяди Боба. Этот принцип педантичен, крайне непрагматичен и имеет ограниченную связь с реальностью.
user949300
12

Ответ Кейт Грегори очень хорошо, но рассмотрим другую ситуацию , когда новое требование может быть удовлетворено сравнительно небольшое изменение в существующем Invoiceклассе. Например, допустим, новое поле должно быть добавлено в PDF-файл счета. Согласно OCP, мы все равно должны создать новый подкласс, даже если новое поле можно добавить в существующую реализацию, изменив несколько строк кода.

В моем понимании, OCP отражает реальность 80-х и начала 90-х годов, когда в проектах часто даже не использовался контроль версий, тем более автоматизированные регрессионные тесты или преимущества сложных инструментов рефакторинга. OCP была попыткой избежать риска взлома кода, который был вручную протестирован и запущен в производство. Сегодня у нас есть более эффективные способы управления риском взлома работающего программного обеспечения (а именно, системы контроля версий, TDD и автоматизированное тестирование, а также инструменты рефакторинга).

Рожерио
источник
2
Да, потому что на практике невозможно создать класс, который можно расширять для всех возможных вариантов будущего, если вы не сделаете все методы защищенными (что отстой, а также нарушает принцип YAGNI, который гораздо более важен, чем O / C). Дито).
Мартин Уикман,
«Согласно OCP, мы все равно должны создать новый подкласс, даже если новое поле можно добавить в существующую реализацию, изменив несколько строк кода». Действительно? Почему бы не добавить новые поля или новые методы? Важным моментом является то, что вы только добавляете (расширяете), а не меняете то, что уже есть.
Джорджио
Я думаю, что принцип имеет смысл при работе со стандартными библиотеками / фреймворками. Вы не хотите открывать и изменять хорошо зарекомендовавшие себя фрагменты кода. В противном случае все дело в постоянном рефакторинге и тестировании, тестировании, тестировании.
mastaBlasta
@Giorgio Конечно, добавление новых полей или методов является то , что я рекомендовал бы, в большинстве случаев. Но это не расширение , это «модификация»; весь смысл OCP заключается в том, что код должен быть «закрыт для модификации» (то есть без изменений в ранее существовавшем исходном файле), в то время как «открыт для расширения»; и расширение в OCP достигается за счет наследования реализации.
Рожерио
@ Rogério: Почему вы определяете границу между расширением и модификацией на уровне класса? Есть ли для этого особая причина? Я бы предпочел установить его на уровне метода: изменение метода изменяет поведение вашего приложения, добавление (публичного) метода расширяет его интерфейс.
Джорджио
6

Лично я думаю, что этот принцип должен быть взят с щепоткой соли. Кодекс органичен, предприятия меняются и код меняется в зависимости от потребностей бизнеса с течением времени.

Мне очень трудно осознать тот факт, что абстракция является ключевой. Что если абстракция изначально была неправильной? Что если бизнес-функция значительно изменилась?

Этот принцип по существу гарантирует, что ОРИГИНАЛЬНЫЕ намерения и поведение проекта никогда не должны изменяться. Это, вероятно, работает для тех, у кого есть общедоступные API, и у их клиентов возникают проблемы с тем, чтобы не отставать от новых выпусков и некоторых других крайних случаев. Однако, если компания владеет ВСЕМ кодом, я оспариваю этот принцип.

Наличие хорошего тестового покрытия вашего кода должно сделать рефакторинг вашей кодовой базы легким. Это означает, что можно ошибиться - ваши тесты помогут вам улучшить дизайн.

Сказав это, если нет никаких тестов, то этот принцип является здравым.

user126776
источник