Принцип Открытого Закрытия (OCP) против Принципа Инверсии Зависимостей (DIP)

12

Я пытался понять разницу между Open Closed Principle (OCP) и Dependency Inversion Princible (DIP).

Основываясь на исследованиях, которые я провел в Интернете, я пришел к выводу, что «DIP - это один из вариантов, с помощью которого мы можем достичь OCP».

Я прав на это?

Можете ли вы привести пример, который не следует DIP, но следует OCP?

Jitendar
источник

Ответы:

16

Ранее я написал ответ о принципах открытого и закрытого типа (OCP) против принципа замещения Лискова (LSP), и оба эти принципа во многом связаны друг с другом, но концептуально отличаются друг от друга некоторыми выдуманными примерами наличия одного, но не другого. Из-за этого ответа я лишь кратко коснусь OCP и еще глубже расскажу о DIP и о том, что делает этот тик.

Давайте попробуем обсудить, как OCP связывается и отличается от Принципа инверсии зависимости (DIP), сначала объяснив различные принципы.

Принцип обращения зависимостей

Читая Принципы OOD дяди Боба, вы обнаружите, что DIP гласит следующее:

Зависит от абстракций, а не от конкрементов.

Абстракции в Java просто достигаются interfaceи abstractключевыми словами, то есть у вас есть «контракт» для некоторых программного объекта , что код должен следовать. Некоторые языки программирования не имеют возможности явно устанавливать поведение для кода, чтобы следовать, поэтому абстракции должны выполняться в более ручном режиме, а не компилятор поможет вам выполнить контракт. Например, в C ++ у вас есть классы с виртуальными методами и динамические языки программирования, такие как Javascript, вы должны убедиться, что вы используете объекты таким же образом (хотя в случае Javascript это было расширено в TypeScript, который добавляет систему типов, чтобы помочь вам с написанием контрактов, что проверяется компилятором).

Название включает в себя термин «инверсия», потому что традиционно (вы знаете, в старые темные времена программирования) вы писали программные структуры, которые имели модули более высокого уровня в зависимости от модулей низкого уровня. Например, имело смысл иметь ButtonAtKitchenвходные данные для обработки KitchenLamp1и KitchenLamp2. К сожалению, это сделало программное обеспечение намного более конкретным, чем нужно, и граф объектов выглядел бы так:

ButtonAtКухонные ручки KitchenLamp1 и KitchenLamp2

Поэтому, когда вы делаете программное обеспечение более общим, добавляя «контракты». Обратите внимание, как стрелки в графе объектов «инвертируют» направление. Вот кухонные светильники теперь зависят от Button. Другими словами, детали теперь зависят от абстракций, а не наоборот.

KitchenButton теперь имеет абстракцию IButton, от которой зависят кухонные лампы

Таким образом, у нас есть более общее определение DIP, также подробно изложенное в оригинальной статье DIP дядюшки Боба .

A. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракции. Б. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Открыто-Закрытый Принцип

В продолжение принципов дяди Боба вы обнаружите, что OCP утверждает следующее:

Вы должны иметь возможность расширять поведение классов, не изменяя его.

Одним из примеров достижения этого является использование шаблона «Стратегия», в котором Contextкласс закрыт для изменений (т. Е. Вы вообще не можете изменить его внутренний код), но также открыт для расширения благодаря взаимодействующим зависимостям (т. Е. Классам стратегии).

В более общем смысле любой модуль построен так, чтобы его можно было расширять с помощью его точек расширения.

Модуль с точками расширения

OCP похож на DIP, верно?

Нет , не совсем.

Хотя они оба обсуждают абстракции, они концептуально разные. Оба принципа рассматривают разные контексты: OCP для одного конкретного модуля и DIP для нескольких модулей. Вы можете достичь и того и другого одновременно с большинством шаблонов проектирования Gang of Four, но вы все равно можете уклониться от пути.

В упомянутом выше примере DIP, при использовании кнопок и кухонных ламп, ни одна из кухонных ламп не является расширяемой (и в настоящее время нет каких-либо требований, объясняющих их необходимость). Дизайн ломает OCP, но следует DIP .

Обратным (и надуманным) примером может быть выдвижение кухонной лампы (с точкой расширения, похожей на a LampShade), но кнопка все еще зависит от ламп . Это нарушает DIP, но следует OCP .

Не волнуйся, бывает

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

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

Spoike
источник