Принцип открытости закрыт

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

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

Кроме того, тот же вопрос для добавления свойств и полей в базовый класс.


person Ivan    schedule 28.06.2017    source источник
comment
Есть возможность вместо этого внести поправку в интерфейс? И чтобы ответить на ваш вопрос, я бы рассматривал это как изменение, а не расширение. Что касается методов, рассматривали ли вы методы расширения, которые по определению расширяют исходную функциональность?   -  person Konrad Viltersten    schedule 28.06.2017
comment
Принцип «открытость-закрытие» на самом деле означает, что если вы хотите добавить в процесс дополнительных функций, вам не нужно менять исходный процесс.   -  person Callum Linington    schedule 28.06.2017
comment
@Konrad Viltersten Я могу ошибаться, но я думал, что методы расширения предпочтительнее, только если вы хотите расширить функциональность класса, которым вы не владеете.   -  person Ivan    schedule 28.06.2017
comment
Я понимаю OCP следующим образом: ваша система должна быть спроектирована таким образом, чтобы, если вы хотите ее расширить, вы пишете новый код вместо того, чтобы переписывать старый код.   -  person Dmitry Pavlushin    schedule 28.06.2017
comment
Я не согласен с большинством голосов, которые утверждают, что этот вопрос основан на мнении. Мы обсуждаем устоявшийся принцип SOLID и вопрос о том, нарушает ли определенное действие этот принцип. Это не мнение.   -  person Flater    schedule 28.06.2017
comment
Что делает этот метод? Какова ответственность базового класса и дочернего класса? Вы не можете применять правила проектирования одеял без контекста. Есть обстоятельства, при которых добавление этого метода (и т. Д.) Допустимо, а в других - нет. Это может быть связано с OCP, хотя Принцип единой ответственности здесь также важен.   -  person Liam    schedule 28.06.2017
comment
@Flater Всегда есть несколько пользователей, которые скорее предполагают, что это материал, основанный на мнении, если только они не считают иначе. Я много раз обсуждал этот вопрос. Часто результат заражения указывает на то, что решение сообщества - закрыть его. (Интересно, что в случае, если решение сообщества снова открыло проблему, те же люди подвергли сомнению процесс. Отсюда и зараженная часть, хе-хе.)   -  person Konrad Viltersten    schedule 28.06.2017
comment
Я думаю, сначала вы должны понять наследование, актуален ли новый метод для всех классов / специализаций или он специфичен для одной специализации? это подскажет вам, куда его положить. Во-вторых, добавление другого метода к базовому классу, на мой взгляд, следует рассматривать как расширение, а не модификацию, поскольку вы не меняете внутреннюю работу существующего кода, а вводите новые вещи. Эта новая функция актуальна для всех подписок.   -  person Mohammed Sohail Ebrahim    schedule 28.06.2017
comment
Что ж, да, это определенно относится только к базовому классу, и в основном поэтому я сомневался. Я хотел поместить его в базовый класс, чтобы соответствовать принципу единой ответственности.   -  person Ivan    schedule 29.06.2017


Ответы (1)


TL; DR
Я думаю, что добавление нового метода по сути означает, что вы не можете нарушать OCP; поскольку OCP в основном фокусируется на безрассудном переопределении существующих методов.


Пример:
Базовый класс Calculator наследуется MyCustomCalculator. Базовый класс имеет только методы для сложения, вычитания, умножения и деления двух чисел.
Создание нового метода в MyCustomCalculator, например CalculateAverage(params int[]) не нарушает OCP. Он не изменяет логику каких-либо существующих методов, а просто расширяет методы, предоставляемые объектом калькулятора.

Новые методы по своей сути не меняют старые методы. Следовательно, они не изменяют существующую логику; затем просто расширьте то, что доступно.

Так что нет, это (по сути) не нарушает OCP.

Нарушение OCP практически невозможно, поскольку создание чего-то нового (по сути) полностью отличается от изменения того, что существует.


Однако на некоторые вещи следует обратить внимание:

  • Я могу придумать одно (спорное) исключение из этого: перегрузка. Если вы создаете новый метод, который перегружает существующий, вы несете ответственность за соблюдение правил перегрузки. В частности, это означает, что перегруженные методы должны выполнять ту же задачу, что и другие (перегруженные методы просто основаны на разных входных параметрах, но их обработка и вывод должны быть функционально эквивалентными). Хотя я не уверен на 100%, является ли это нарушение OCP (потому что он неправильно расширяет базовый класс) или SRP (потому что это создает неоднозначную ответственность в отношении обработки / вывода перегруженных методов. ). Кажется, и то и другое.
  • Тот же принцип применяется, даже если ваш новый метод не перегружает существующий. Достаточно ли актуален новый метод для ответственности (намерения / цели) базового класса? Потому что, если он сильно отличается, вы, возможно, нарушаете SRP.

ИЗМЕНИТЬ

Я пропустил часть вашего вопроса

Можно ли добавить метод к базовому классу или мы также должны создать новый класс, который наследует базовый класс, а затем поместить туда новый метод?

Это зависит от контекста. Для примера Calculator / MyCustomCalculator / CalculateAverage(), который я предоставил; Я бы инстинктивно добавил его в базовый класс, поскольку вычисление средних значений - это базовая операция, относящаяся к обязанностям калькулятора.

Однако, если бы мы обсуждали добавление метода, который относится только к комплексным числам (MethodForComplexNumbers()), то я бы поддержал создание ComplexNumberCalculator, который наследуется от Calculator и реализует MethodForComplexNumbers().

Однако я думаю, что это в основном связано с SRP, а не OCP.

person Flater    schedule 28.06.2017
comment
Возможно, стоит обсудить несколько угловых случаев: например, если класс участвует в каком-либо виде (де-) сериализации, тогда добавления к классу может быть достаточно, чтобы разрушить существующие сериализованные данные. - person grek40; 04.08.2017
comment
@ grek40: Вы уверены? Потому что десериализация данных должна действительно пытаться установить свойства, которые она находит в сериализованных данных. Пока эти свойства все еще присутствуют в классе, он не должен заботиться о дополнительных свойствах, которые вы добавили. Тем более, что для сериализации требуется конструктор без параметров, любые свойства, которые не устанавливаются, уже должны иметь работоспособное значение по умолчанию. (также, как правило, SOLID имеет тенденцию разваливаться в крайних случаях. Это хороший ориентир, но не хороший глобальный закон). - person Flater; 04.08.2017
comment
@ grek40: Единственная проблема, которую я вижу, связана с сериализацией обновленного класса и последующей попыткой его десериализации с помощью устаревшей версии. Но эту проблему невозможно избежать, если не считать чрезмерной логики попытки / отлова (которую я не сторонник). - person Flater; 04.08.2017
comment
Конечно, хороший сериализатор просто проигнорирует лишние данные ... но даже несмотря на то, что в C # этих подводных камней меньше, чем в других языках, все еще существуют такие вещи, как двоичный форматтер, требуя от вас удовлетворения их особых потребностей, чтобы не ломать вещи. Как уже было сказано, это крайний случай, а не основной сценарий использования. Еще один немного более распространенный пример: рассмотрим таблицу пользовательского интерфейса с автоматически сгенерированными столбцами - пользовательский интерфейс изменится, просто расширив класс элемента данных дополнительными свойствами. - person grek40; 04.08.2017
comment
Любое изменение - это компромисс с возможными побочными эффектами, которые могут появиться в сочетании с отражением - в конце концов, разработчик должен решить, действительно ли эти вещи вызывают беспокойство в рассматриваемом случае. Было бы неверно утверждать общее правило расширение не критично без каких-либо ограничений. - person grek40; 04.08.2017
comment
@ grek40: Я не говорю, что вы ошибаетесь (двоичная сериализация - хороший пример), но, похоже, это выходит за рамки, что касается SOLID. SOLID не влияет на существующие сохраненные данные. SOLID фокусируется только на передовых методах в разумных пределах. Я никогда не видел ни одного проекта (кроме небольших примеров), который полностью придерживался бы принципов SOLID до педантичной степени. Вы проходите точку, в которой усилия по его реализации больше не стоят той выгоды, которую вы получаете от этого. - person Flater; 04.08.2017