Недавно я читал веб-сайт о разработке чистого кода (здесь я не размещаю ссылку, потому что она не на английском языке).
Одним из принципов, рекламируемых этим сайтом, является принцип Open Closed : каждый программный компонент должен быть открыт для расширения и закрыт для модификации. Например, когда мы реализовали и протестировали класс, мы должны только изменить его, чтобы исправить ошибки или добавить новые функциональные возможности (например, новые методы, которые не влияют на существующие). Существующая функциональность и реализация не должны быть изменены.
Я обычно применяю этот принцип, определяя интерфейс I
и соответствующий класс реализации A
. Когда класс A
стал стабильным (реализован и протестирован), я обычно не слишком сильно его модифицирую (возможно, совсем не изменяю), т.е.
- Если появляются новые требования (например, производительность или совершенно новая реализация интерфейса), которые требуют больших изменений в коде, я пишу новую реализацию
B
и продолжаю использоватьA
до тех пор, покаB
она не является зрелой. КогдаB
наступит зрелость, все, что нужно, - это изменить способI
создания экземпляра. - Если новые требования также предполагают изменение интерфейса, я определяю новый интерфейс
I'
и новую реализациюA'
. Таким образомI
,A
будут заморожены и остаются реализация для производственной системы до тех пор,I'
иA'
не достаточно стабильна , чтобы заменить их.
Поэтому, учитывая эти наблюдения, я был немного удивлен, что веб-страница тогда предложила использовать сложные рефакторинги , «... потому что невозможно написать код непосредственно в его окончательной форме».
Нет ли противоречия / конфликта между применением открытого / закрытого принципа и предложением использования сложных рефакторингов в качестве лучшей практики? Или идея в том, что можно использовать сложные рефакторинги во время разработки класса A
, но когда этот класс был успешно протестирован, его следует заморозить?
Принцип Open-Closed - это скорее показатель того, насколько хорошо разработано ваше программное обеспечение ; не принцип, чтобы следовать буквально. Это также принцип, который помогает нам избежать случайного изменения существующих интерфейсов (классов и методов, которые вы вызываете, и того, как вы ожидаете, что они будут работать).
Цель состоит в том, чтобы написать качественное программное обеспечение. Одним из этих качеств является расширяемость. Это означает, что можно легко добавлять, удалять и изменять код, причем эти изменения, как правило, ограничиваются минимальным количеством существующих классов. Добавление нового кода менее рискованно, чем изменение существующего кода, поэтому в этом отношении полезно сделать Open-Closed. Но о каком коде мы говорим? Преступление нарушения OC намного меньше, когда вы можете добавлять новые методы в класс вместо необходимости изменять существующие.
OC это фрактал . Это яблоки на всех глубинах вашего дизайна. Все предполагают, что это применяется только на уровне класса. Но это одинаково применимо на уровне метода или на уровне сборки.
Слишком частое нарушение ОК на соответствующем уровне наводит на мысль, что, возможно, пришло время провести рефакторинг . «Соответствующий уровень» - это суждение, которое имеет отношение к вашему общему дизайну.
Следование Open-Closed буквально означает, что количество классов взорвется. Вы будете создавать (заглавная буква "I") интерфейсы без необходимости. В итоге вы получите кусочки функциональности, распределенные по классам, а затем вам придется написать намного больше кода, чтобы связать все это вместе. В какой-то момент вы поймете, что изменение исходного класса было бы лучше.
источник
Принцип Open-Closed, кажется, является принципом, который появился до того, как TDD стал более распространенным. Идея заключается в том, что рефакторинг кода рискованно, потому что вы можете что-то сломать, поэтому безопаснее оставить существующий код как есть и просто добавить к нему. В отсутствие тестов это имеет смысл. Недостатком этого подхода является атрофия кода. Каждый раз, когда вы расширяете класс, а не проводите его рефакторинг, вы получаете дополнительный слой. Вы просто набираете код сверху. Каждый раз, когда вы набираете больше кода, вы увеличиваете вероятность дублирования. Представить; в моей кодовой базе есть служба, которую я хочу использовать, я нахожу, что у нее нет того, что я хочу, поэтому я создаю новый класс, чтобы расширить его и включить мои новые функциональные возможности. Другой разработчик приходит позже и также хочет использовать тот же сервис. К сожалению, они не Я не понимаю, что моя расширенная версия существует. Они кодируют исходную реализацию, но им также нужна одна из функций, которые я кодировал. Вместо использования моей версии они теперь также расширяют реализацию и добавляют новую функцию. Теперь у нас есть 3 класса, оригинальная и две новые версии, которые имеют дублированные функциональные возможности. Следуйте принципу «открыто / закрыто», и это дублирование будет накапливаться в течение всего жизненного цикла проекта, приводя к ненужно сложной кодовой базе.
С хорошо протестированной системой нет необходимости страдать от атрофии кода, вы можете безопасно реорганизовать код, позволяющий вашему проекту воспринимать новые требования, вместо того, чтобы постоянно искать новый код. Этот стиль разработки называется эмерджентным дизайном и приводит к основам кода, способным поддерживать хорошую форму в течение всей жизни, а не постепенно собирать ненужные вещи.
источник
По словам непрофессионала:
A. O / C принцип означает , что специализация должна быть сделана путем расширения, не изменяя класс для размещения для специализированных нужд.
Б. Добавление отсутствующей (не специализированной) функциональности означает, что дизайн не был завершен, и вы должны добавить его в базовый класс, очевидно, не нарушая контракт. Я думаю, что это не нарушает принцип.
C. Рефакторинг не нарушает принцип.
Когда дизайн созревает , скажем, через некоторое время в производстве:
источник
Для меня принцип Open-Closed - это руководство, а не жесткое и быстрое правило.
Что касается открытой части принципа, конечные классы в Java и классы в C ++ со всеми конструкторами, объявленными как private, нарушают открытую часть принципа open-closed. Для финальных классов есть хорошие сценарии использования (примечание: твердые, а не твердые). Проектирование для расширяемости важно. Тем не менее, это требует значительного предвидения и усилий, и вы всегда обходите линию нарушения YAGNI (вам это не понадобится) и привносят в код запах спекулятивной общности. Должны ли ключевые программные компоненты быть открыты для расширения? Да. Все? Это само по себе является умозрительной общностью.
Что касается закрытой части, то при переходе с версии 2.0 на 2.1 до 2.2 на 2.3 какого-либо продукта не рекомендуется изменять поведение. Пользователям действительно не нравится, когда каждый второстепенный выпуск нарушает их собственный код. Однако по пути часто обнаруживается, что первоначальная реализация в версии 2.0 была принципиально нарушена или что внешние ограничения, ограничивающие первоначальный дизайн, больше не применяются. Вы смеете над этим и поддерживаете этот дизайн в выпуске 3.0, или вы делаете 3.0 не обратно совместимым в некотором отношении? Обратная совместимость может быть огромным ограничением. Главные границы выпуска - это место, где допустимо нарушение обратной совместимости. Вы должны знать, что это может расстроить ваших пользователей. Должно быть хорошее обоснование того, почему необходим этот разрыв с прошлым.
источник
Рефакторинг по определению меняет структуру кода без изменения поведения. Поэтому при рефакторинге вы не добавляете новые функции.
То, что вы сделали в качестве примера для принципа Open Close, звучит нормально. Этот принцип заключается в расширении существующего кода новыми функциями.
Однако не поймите этот ответ неправильно. Я не имею в виду, что вы должны выполнять функции или выполнять рефакторинг только для больших порций данных. Наиболее распространенный способ программирования - это сделать что-то особенное, чем сразу же выполнить небольшой рефакторинг (конечно, в сочетании с тестами, чтобы убедиться, что вы не изменили поведение). Сложный рефакторинг не означает «большой» рефакторинг, это означает применение сложных и продуманных методов рефакторинга.
О ТВЕРДЫХ принципах. Это действительно хорошие рекомендации для разработки программного обеспечения, но они не являются религиозными правилами, которым нужно слепо следовать. Иногда, после добавления второй, третьей и n-й функций, вы понимаете, что ваш первоначальный дизайн, даже если он учитывает Open-Close, не учитывает другие принципы или требования к программному обеспечению. Существуют моменты в развитии дизайна и программного обеспечения, когда необходимо сделать более сложные изменения. Все дело в том, чтобы как можно скорее найти и реализовать эти проблемы и как можно лучше применять методы рефакторинга.
Идеального дизайна не бывает. Не существует такого дизайна, который мог бы и должен уважать все существующие принципы или шаблоны. Это кодирование утопии.
Я надеюсь, что этот ответ помог вам в вашей дилемме. Не стесняйтесь просить разъяснений, если это необходимо.
источник
B
и, когда она будет готова, замените старую реализациюA
новой реализациейB
(это одно из применений интерфейсов).A
Код может служить основой дляB
кода, и тогда я могу использовать рефакторингB
кода во время его разработки, но я думаю, что уже протестированныйA
код должен оставаться замороженным.B
построен на кодеA
как эволюцияA
, то, когдаB
он выпущен, егоA
следует удалить и никогда больше не использовать. Клиенты, которые ранее использовали,A
будут просто использовать,B
не зная об изменении, так как интерфейсI
не был изменен (может быть, здесь немного принципа подстановки Лискова? ... L из SOLID)Насколько я понимаю - если вы добавите новые методы в существующий класс, то это не сломает OCP. однако я немного запутался с добавлением новых переменных в класс. Но если вы измените существующий метод и параметры в существующем методе, то он обязательно нарушит OCP, потому что код уже протестирован и передан, если мы намеренно изменим метод [При изменении требования], тогда это будет проблемой.
источник