принцип открыт-закрыт - рефакторинг для создания базового класса на основе новых функций

Поэтому, когда был написан исходный код, нужен был только, скажем, класс LabTest. Но теперь скажем, что у нас есть новые требования, чтобы добавить, например, RadiologyTest, EKGTest и т. Д.

У этих классов много общего, поэтому имеет смысл иметь базовый класс.

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

Является ли это нарушением принципа открытости-закрытости? (LabTest модифицируется).


person krprasad    schedule 30.04.2014    source источник


Ответы (4)


Я думаю, вы можете взглянуть на это с двух точек зрения: существующие требования и новые требования.

Если существующие требования не покрывают потребности в таких изменениях, я бы сказал, что на основе этих требований LabTest не нарушает OCP.

С новыми требованиями вам нужно добавить функциональность, которая не подходит для реализации LabTest. Добавление его в OCP нарушит SRP. Теперь требования создают новый вектор изменений, который заставит вас провести рефакторинг LabTest, чтобы сохранить его OCP. Если вам не удастся провести рефакторинг LabTest, это приведет к нарушению SRP и OCP. При рефакторинге помните о новом векторе изменений в любых классах, которые вы создаете или изменяете.

person brader24    schedule 01.05.2014

У этих классов много общего, поэтому имеет смысл иметь базовый класс.

Я думаю, вы нарушаете SRP. В конце концов, если каждый класс выполняет одну задачу, как два или более могут быть такими похожими? Если есть задача, которую они оба выполняют одинаково, то это отдельная задача и должна выполняться другим классом.

Итак, я бы сказал, сначала выполните рефакторинг LabTest на его составные части (надеюсь, у вас есть модульные тесты!). Затем, когда вы придете писать RadiologyTest, EKGTest они могут повторно использовать те части, которые имеют для них смысл. Это также известно как композиция по наследованию.

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

person weston    schedule 01.05.2014
comment
В некотором роде мы говорим, что OCP плохо работает при изменении ожиданий (требований) от системы. Если бы я просто следовал OCP, я должен был оставить LabTest в покое и продублировать всю логику / код, которые будут использоваться в RadiologyTest и EKGTest, что нарушает принцип DRY. - person krprasad; 02.05.2014
comment
Что ж, я говорю, потому что вы не использовали другие правила SOLID, у вас большой класс, которому не нравится дублирование. Но у вас нет хрустального шара, поэтому это произойдет (т.е. часто требуется расширить какую-то часть системы, чего вы не предусмотрели). Лучше всего сначала провести рефакторинг (используя свой набор тестов), чтобы сделать систему расширяемой в этом отношении, а затем расширить ее. Тогда, когда это произойдет во второй или третий раз, вы будете готовы к этому. - person weston; 02.05.2014
comment
@weston Я думаю, это лежит в основе того, что я пытался сказать. У тебя никогда не бывает хрустального шара. OCP не говорит, что вы никогда не должны изменять класс, особенно при изменении требований, но когда вы понимаете, что класс должен измениться, вы реорганизуете его таким образом, чтобы он действительно соответствовал OCP для будущих изменений. подобного типа. И я согласен, вам следует сначала выполнить рефакторинг, чтобы код соответствовал OCP для данного типа изменений, а затем расширить его. - person brader24; 02.05.2014

Я могу обжечься за этот ответ, но все равно рискну. На мой взгляд (IMO), OCP нельзя придерживаться в чистом смысле слова, как другие принципы, такие как SRP, DIP или ISP.

Если требования изменяются таким образом, что вы должны изменить ответственность класса, чтобы соответствовать их представлению модели предметной области, тогда мы должны изменить этот класс.

IMO, OCP не дает нам повторно использовать код для отслеживания эволюции системы.

Пожалуйста, поправьте меня, если я ошибаюсь.

Обновление: после дальнейших исследований я подумал вот о чем: допустим, я автоматизировал тестирование как на уровне единицы, так и на уровне интеграции, а затем, ИМО, мы должны перепроектировать всю систему, чтобы она соответствовала новой модели, OCP. здесь за дверью. ИМО, цель эволюции системы всегда состоит в том, чтобы избежать взлома (не изменять класс LabTest и соответствующую таблицу БД, чтобы не нарушать старый код [не нарушать OCP], и использовать LabTest для хранения общих данных EKGTest и использовать LabTest внутри EKGTest или EKGTest, унаследованный от LabTest, будет взломом, IMO) будет и для того, чтобы система представляла свою модель как можно точнее.

person krprasad    schedule 02.05.2014
comment
Я позволю модераторам решить правильный ответ. У меня нет проблем с выбором, поскольку в результате этого обсуждения мои сомнения развеялись. - person krprasad; 05.05.2014

Я думаю, что принцип «открытость-закрытость» (в любом случае изложенный дядей Бобом в сравнении с Бертраном Мейерсом) не в том, чтобы никогда не изменять классы (если программное обеспечение никогда не собиралось менять, это могло бы также быть аппаратным).

& в вашем собственном случае, я не думаю, что вы нарушаете OCP, поскольку вы упомянули, что все ваши способы использования вашего класса зависят от абстракции LabTest, а не от реализации RadiologyTest.

Из вводной статьи дяди Боба у него есть пример DrawAllShapes класса, который, если он был разработан в OCP, не нужно менять каждый раз, когда в систему добавляется новый подкласс Shape. Что касается того, на каком уровне вы его применяете, дядя Боб говорит, что ...

Должно быть понятно, что ни одна значимая программа не может быть закрыта на 100%. Например, подумайте, что случилось бы с функцией DrawAllShapes из листинга 2, если бы мы решили, что все Circles должны быть нарисованы перед любым Squares. Функция DrawAllShapes не закрывается от подобных изменений. В общем, каким бы «закрытым» ни был модуль, всегда будет какое-то изменение, от которого он не закрыт.

Поскольку закрытие не может быть полным, оно должно быть стратегическим. То есть дизайнер должен выбрать виды изменений, от которых следует закрыть свой дизайн.

Я бы не стал читать «закрыто для модификации» как «без рефакторинга», более того, вы должны проектировать свои классы таким образом, чтобы другие классы не могли вносить изменения, которые могут повлиять на вас - например, применение базового объектно-ориентированного подхода - инкапсуляция через геттеры / сеттеры и частные переменные-члены.

person anotherdave    schedule 11.05.2014
comment
да, правило с неопределенным исключением. Итак, как мы решаем, когда нарушается OCP? Как я уже сказал, это одно из правил, которому я буду уделять меньше всего внимания. Пока мы следуем другим принципам дизайна и руководствуемся здравым смыслом, у нас все будет в порядке. - person krprasad; 12.05.2014
comment
Я думаю, что ему может потребоваться больше гибкости, чем пытаться применить его в черно-белой форме (было ли это нарушено - да / нет). Как и в случае со всеми принципами дизайна, они не связаны с необходимостью их неукоснительного следования, а скорее дают вам эвристику. Я бы предпочел применить OCP к изменениям, которые, по моему мнению, вероятны в первоначальном дизайне, и к аналогичным изменениям при рефакторинге для непредвиденных требований. Также стоит взглянуть на это сообщение в блоге: blog.8thlight.com /uncle-bob/2013/03/08/AnOpenAndClosedCase.html - person anotherdave; 13.05.2014