OOP Course
Сьогодні

Підрозділ 20.3

OCP — Принцип відкритості/закритості

20.3. OCP — Принцип відкритості/закритості Open/Closed Principle OCP сформулював Бертран Мейєр Bertrand Meyer у 1988 році, а пізніше переосмислив і популяризував Роберт Мартін. Принцип звучить так: «Програмні с

20.3. OCP — Принцип відкритості/закритості

Open/Closed Principle (OCP) сформулював Бертран Мейєр (Bertrand Meyer) у 1988 році, а пізніше переосмислив і популяризував Роберт Мартін. Принцип звучить так: «Програмні сутності (класи, модулі, функції) мають бути відкриті для розширення, але закриті для модифікації».

На перший погляд ця фраза виглядає парадоксально: як можна розширити поведінку класу, не змінюючи його? Відповідь полягає у правильному використанні абстракцій — інтерфейсів і базових класів. Новий варіант поведінки додається через нову реалізацію абстракції (новий клас), а не через правку існуючого коду.

Чому це важливо? Існуючий код — це код, який вже протестований, задокументований і розгорнутий у виробництво. Кожна його зміна несе ризик: навіть невелика правка може зламати щось, що раніше працювало коректно. OCP мінімізує цей ризик: нова функціональність з'являється у новому коді, а старий залишається незайманим.

Порушення OCP: switch і if-ланцюги над типом

Найпоширеніше порушення OCP — це код, що перевіряє тип об'єкта через switch або if-ланцюги і виконує різну логіку залежно від цього типу. Такий патерн означає, що при появі кожного нового типу доведеться відкривати і змінювати вже існуючий клас.

Проблема не в тому, що switch-вирази погані самі по собі. Проблема в тому, що тут AppointmentType виступає точкою варіативності — місцем, де система повинна підтримувати розширення. І ця точка варіативності не захищена абстракцією: вона відкрита для прямого перебирання типів скрізь у коді.

До OCP — switch-ланцюги вимагають зміни існуючого коду

Рефакторинг: абстракція як точка розширення

OCP вирішується введенням абстракції для точки варіативності. Замість того, щоб клас-калькулятор знав про всі типи прийомів — кожен тип прийому знає свою вартість. Новий тип = новий клас, що реалізує інтерфейс. Калькулятор не змінюється.

Зверніть увагу: CostCalculator не змінився при додаванні VipAppointment. Ми лише написали новий клас, що реалізує IAppointmentType. Старий код — протестований, стабільний — залишився незайманим.

Після OCP — новий тип = новий клас, без зміни калькулятора

OCP і поліморфізм: зв'язок із попередніми темами

OCP безпосередньо спирається на поліморфізм — механізм, що дозволяє CostCalculator.Calculate приймати будь-яку реалізацію IAppointmentType і викликати правильну версію CalculateCost. Це і є «відкритість для розширення»: нові реалізації інтерфейсу автоматично підтримуються існуючим кодом.

Важлива деталь: OCP не вимагає, щоб клас був закритий від самого початку. Мартін пояснював це через «правило трьох»: коли одна й та ж зміна відбувається втретє — настав час виявити абстракцію і закрити клас від цієї зміни. Передчасна абстракція — теж помилка.

Стратегія (Strategy Pattern) — класичне застосування OCP

OCP часто реалізується через патерн Стратегія (Strategy): алгоритм виноситься в інтерфейс, а конкретні реалізації передаються ззовні. Це дозволяє замінювати поведінку без зміни класу, що її використовує.

BillingService.GenerateBill не змінювався при додаванні SeasonalDiscount. Це OCP у дії.

OCP через успадкування: Template Method

Ще один класичний спосіб реалізувати OCP — шаблонний метод (Template Method): базовий клас визначає алгоритм, а підкласи перевизначають конкретні кроки.

Коли OCP особливо важливий

OCP найважливіший у трьох ситуаціях:

Бібліотеки та фреймворки: код, що використовується іншими командами або проектами, не може бути змінений клієнтами — тому він має бути розширюваним через абстракції.

Місця з частими змінами: якщо ви вже тричі змінювали один і той самий клас через схожі причини (новий тип знижки, новий формат звіту, новий канал сповіщень) — час закрити його від цих змін.

Плагін-архітектури: системи, де поведінка налаштовується зовні (наприклад, медичні модулі у клінічній системі), проектуються через OCP від самого початку.

Межі OCP: не все треба абстрагувати

OCP — не заклик до надмірної абстракції. Абстракція коштує: вона ускладнює код, збільшує кількість файлів і вимагає більше для розуміння. Абстракція виправдана лише там, де очікується реальна варіативність.

Практичне правило: не закривайте клас від змін, поки не побачите реальну потребу в розширенні. Замість цього пишіть простий код, а коли з'являється другий або третій різновид — рефакторингом виявляйте абстракцію. Це узгоджується зі принципом «YAGNI» (You Aren't Gonna Need It — вам це не знадобиться) і зі стилем «Agile», в якому OCP і з'явився.

Розроблено Tomka Yurii · © 2026 ·