Раніше я писав відповідь на принципі відкритого закриття (OCP) проти принципу заміни Ліскова (LSP), і обидва ці принципи багато в чому стосуються один одного, але все ще концептуально відрізняються з деякими надуманими прикладами наявності одного, а не іншого. Через цю відповідь я лише коротко торкнуся OCP та занурююся вглиб DIP і що робить цей галочку.
Давайте спробуємо обговорити, як OCP відноситься та відрізняється від принципу інверсії залежності (DIP), спочатку пояснивши різні принципи.
Принцип інверсії залежності
Читаючи "Принципи дядька Боба" OOD, ви побачите, що DIP визначає таке:
Залежить від абстракцій, а не від конкрементів.
Абстракції в Java просто досягаються interface
і abstract
ключовими словами, тобто у вас є «контракт» для деяких програмного об'єкта , що код повинен слідувати. Деякі мови програмування не мають можливості чітко встановити поведінку для коду, який слід виконувати, тому абстракції повинні дотримуватися більш ручним способом, а не тим, що компілятор допоможе виконувати контракт. Наприклад, у C ++ у вас є класи з віртуальними методами та динамічними мовами програмування, такими як Javascript, ви повинні переконатися, що ви використовуєте об’єкти однаково (хоча у випадку Javascript це було розширено в TypeScript, що додає систему типів, щоб допомогти вам з написанням договорів, що перевіряється укладачем).
Назва включає термін "інверсія", оскільки традиційно (ви знаєте в старі темні століття програмування) ви писали програмні структури, які мали модулі вищого рівня залежно від модулів низького рівня. Наприклад, має сенс мати ButtonAtKitchen
керовані входи для KitchenLamp1
та та KitchenLamp2
. На жаль, це зробило програмне забезпечення набагато конкретнішим, ніж потрібно, і об'єктний графік виглядатиме так:
Тож коли ви робите програмне забезпечення більш загальним, додаючи "контракти". Зауважте, як стрілки на графіку об'єкта "інвертують" напрямок. Ці кухонні світильники тепер залежать від Button
. Іншими словами, тепер деталі залежать від абстракцій, а не навпаки.
Таким чином, ми маємо більш загальне визначення DIP, також детально описане в оригінальній статті DIP дядька Боба .
A. Модулі високого рівня не повинні залежати від модулів низького рівня. І те й інше повинно залежати від абстракції. B. Абстракції не повинні залежати від деталей. Деталі повинні залежати від абстракцій.
Принцип відкритого закриття
Продовжуючи принципи дядька Боба, ви побачите, що OCP констатує наступне:
Ви повинні мати можливість розширити поведінку класів, не змінюючи її.
Одним із прикладів досягнення цього є використання шаблону стратегії, коли Context
клас закритий для модифікацій (тобто ви взагалі не можете змінити свій внутрішній код), але також відкритий для розширення через співпрацюючі залежності (тобто класи стратегій).
У більш загальному сенсі будь-який модуль побудований для розширення через точки розширення.
OCP схожий на DIP, правда?
Ні , не дуже.
Хоча вони обидва обговорюють абстракції, вони концептуально відрізняються. Обидва принципи розглядають різні контексти, OCP на одному конкретному модулі та DIP на декількох модулях. Ви можете досягти обох одночасно, як і у більшості моделей Gang of Four, але все ж можете відійти від шляху.
У згаданому вище прикладі DIP, за допомогою кнопок та кухонних світильників жоден з кухонних світильників не розширюється (і наразі не передбачено жодних вимог, що пояснюють їх необхідність). Конструкція порушує OCP, але дотримується DIP .
Зворотним (і надуманим) прикладом може бути кухонна лампа, яка може бути розширюваною (при цьому точка розширення є чимось на зразок а LampShade
), але кнопка все ще залежить від ламп . Він порушує DIP, але слідує за OCP .
Не хвилюйтесь, це буває
Це насправді те, що ви часто зустрічаєтесь у виробничому коді, що якась частина може порушити принцип. У більш великих програмних системах (тобто що-небудь більше, ніж у наведених вище прикладах), ви можете порушити один принцип, але зберігати інший, як правило, тому що вам потрібно простий код. На мій погляд, це нормально для невеликих та автономних модулів, оскільки вони є в контексті, пов'язаному з Принципом єдиної відповідальності (SRP).
Після того, як якийсь - то модуль стає складним , хоча ви , швидше за все , потрібно дивитися на нього з усіма принципами на увазі , і перепроектувати або реорганізувати його в який - то добре відомої моделі.