Уточнити відкритий / закритий принцип


25

Як я вже це пояснював, принцип відкритого / закритого типу визначає, що колись написаний код не слід змінювати (окрім виправлень помилок). Але якщо зміни моїх правил бізнесу не слід змінювати код, що впроваджує ці зміни? Я підозрюю, що я щось не розумію, як цей принцип, тому що він не має для мене сенсу.

Відповіді:


22

Це, мабуть, найскладніший із твердих принципів для пояснення. Дозвольте мені спробувати. Уявіть, що ви написали рахунок-фактура, який прекрасно працює і не має помилок. Це робить PDF-рахунок-фактуру.

Потім хтось каже, що хоче отримати рахунок-фактуру HTML із посиланнями в ньому. Ви не змінюєте жодного коду в Рахунку-фактурі, щоб задовольнити цей запит. Натомість ви складаєте інший клас, HTMLInvoice, який робить те, що вони зараз хочуть. Ви використовуєте спадщину, щоб не потрібно писати багато повторюваних кодів у HTMLInvoice.

Старий код, який використовував старий рахунок-фактура, не порушений або по-справжньому впливає. Новий код може використовувати HTMLInvoice. (Якщо ви також робите Ліскову заміну , L твердого, ви можете надати екземпляри HTMLInvoice існуючому коду, який очікує екземплярів рахунків.) Усі живуть щасливо.

Рахунок-фактура закритий для модифікації, відкритий для розширення. І ви повинні заздалегідь написати рахунок-фактуру належним чином, щоб це працювало, btw.


1
Якщо правила ведення бізнесу змінюються, немає припущення, що ви працюєте ідеально без помилок, тому принцип відкритого / закритого не застосовується?
JeffO

Я сам боровся з цим правилом, і те, що пропонує Кейт, - це, головним чином, те, що я на ньому зробив. У бізнесі ви намагаєтеся запрограмувати менші, гнучкіші класи, щоб ви могли їх добре використати. Якщо ви працюєте правильно, ви не хочете їх змінювати. Але вони рідко повністю «робляться», тому деякі модифікації неминучі. Зауважте, що в тексті написано "модуль", але не об'єкт. Я часто успішно застосовую OCP на рівні функцій із чіткими функціями, які роблять одне цілком ідеально і ніколи не потребують змін.
CodexArcanum

1
@Jeff OI розрізняє виправлення помилки (де код не відповідав початковій вимозі, і ніхто не хоче цього як є) та зміну вимог. Якщо мені потрібні PDF-файли, а код створює PDF-файли, помилок немає, хоча я зараз хочу HTML (і зазвичай люди хочуть також HTML, а не замість нього)
Kate Gregory

2
@Winston - це те, що я мав на увазі, коли сказав, що потрібно правильно написати рахунок-фактуру. В ідеалі вже був досить абстрактний рахунок-фактура, і ви успадкували PDFInvoice, очікуючи цього. Якщо ні, то потрібно один раз порушити правило, щоб налаштувати себе, щоб не порушувати його в майбутньому. Так чи інакше, прогнозування майбутніх змін є величезною частиною всього цього - і ось «рецепт слона» і частина цього рецепту.
Кейт Григорій

1
Ваше останнє твердження є найважливішим. Відкритий / закритий - це ідеальний варіант дизайну - і для його досягнення ви повинні отримати дизайн прямо вперед. Не все також потрібно задовольняти відкритим / закритим, але це потужний інструмент, якщо ви можете туди потрапити.
Алекс Фейнман

13

Ви читали статтю «Відкритий принцип » друзів дядька Боба в ObjectMentor? Я думаю, що це одне з кращих пояснень.

Є багато евристики, пов'язаної з об'єктно-орієнтованим дизайном. Наприклад, "всі змінні члена повинні бути приватними", або "глобальні змінні слід уникати", або "використання ідентифікації типу часу виконання (RTTI) небезпечно". Яке джерело цих евристик? Що робить їх справжніми? Чи завжди вони правдиві? У цій колонці досліджено принцип проектування, що лежить в основі цієї евристики - принципу відкритого закритого типу.

Як сказав Івар Якобсон: «Всі системи змінюються протягом свого життєвого циклу. Це потрібно мати на увазі, коли розробляються системи, які, як очікується, триватимуть довше, ніж перша версія. »Як ми можемо створити конструкції, стабільні в умовах змін, які триватимуть довше, ніж перша версія? Бертран Мейєр дав нам рекомендації ще в 1988 році, коли він придумав відомий тепер відкритий принцип. Перефразовуючи його:

ПРОГРАМНІ ЗАБЕЗПЕЧЕННЯ (КЛАСИ, МОДУЛИ, ФУНКЦІЇ, ETC.) ПОВИННІ ВІДКРИТИ ДЛЯ РОЗШИРЕННЯ, АЛЕ ЗАКРІТЛЕНО НА МОДИФІКАЦІЮ.

Коли одна зміна програми призводить до каскаду змін залежних модулів, ця програма виявляє небажані атрибути, які ми стали асоціювати з «поганим» дизайном. Програма стає крихкою, жорсткою, непередбачуваною та невиправданою. Принцип відкритого закриття атакує це дуже прямолінійно. У ньому йдеться про те, що ви повинні проектувати модулі, які ніколи не змінюються . Коли вимоги змінюються, ви розширюєте поведінку таких модулів, додаючи новий код, а не змінюючи старий код, який вже працює.

Опис

Модулі, які відповідають принципу відкритого закритого типу, мають два основні ознаки.

  1. Вони "Відкриті для розширення".
    Це означає, що поведінку модуля можна продовжити. Що ми можемо змусити модуль вести себе по-новому і по-різному, коли змінюються вимоги програми або задовольняють потреби нових програм.
  2. Вони закриті для внесення змін.
    Вихідний код такого модуля є непорушним. Ніхто не може вносити зміни до вихідного коду.

Здавалося б, ці два атрибути суперечать один одному. Нормальним способом розширення поведінки модуля є внесення змін до цього модуля. Як правило, вважається, що модуль, який неможливо змінити, має фіксовану поведінку. Як можна вирішити ці два протилежні атрибути?

Абстракція - це ключ ...


3
Це гарна стаття, що пояснює абстракцію. Хоча слід враховувати фундаментальний момент, і це, в першу чергу, був хороший абстрактний дизайн? У багатьох магазинах є багато застарілого коду, що єдиний спосіб змінити це "модифікація", а не "розширення". Якщо це так, то, ймовірно, слід попрацювати, щоб змінити це, але поки це не відбудеться, ви застрягли в модифікаційному коді.
Майкл К

@Chris, круто - я також рекомендую книгу дядька Боба "Чистий код", якщо вам подобається така штука.
Мартійн Вербург

@Michael - Цілком погоджуюсь, це майже як рефафактор коду, щоб зробити його перевіреним ідеально.
Мартійн Вербург

Стаття дуже добре демонструє важливість абстракції. Але я не розумію зв'язку між абстракціями і намагаюся ніколи не змінювати модулі після того, як їх пишу. Абстракція означає, що я можу змінити модуль X, не вносячи модифікацій в модуль Y. Але це не сенс робити це, щоб я міг змінити модуль X або модуль Y, якщо це потрібно?
Вінстон Еверт

1
Ого. Код непорушний? Я ніколи не був великим шанувальником дядька Боба. Цей головний принцип педантичний, вкрай непрагматичний і має обмежений зв'язок з реальністю.
user949300

12

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

На моє розуміння, OCP відображає реальність 80-х та початку 90-х, де проекти часто навіть не використовували контроль версій, а тим більше мали автоматизовані тести регресії або користь від складних інструментів рефакторингу. OCP була спробою уникнути ризику порушення коду, який був випробуваний вручну та введений у виробництво. Сьогодні у нас є кращі способи управління ризиком зламати робоче програмне забезпечення (а саме системи управління версіями, TDD та автоматизовані тестування та інструменти рефакторингу).


2
Так, тому що на практиці неможливо створити клас, який можна розширити, щоб відповідати всім можливим ф'ючерсам, якщо ви не зробите всі методи захищені (що висмоктує, а також порушує принцип YAGNI, що набагато важливіше, ніж O / C діто).
Мартін Вікман

"Відповідно до OCP, ми все-таки повинні створити новий підклас, навіть якщо нове поле можна було б додати в існуючу реалізацію, змінивши кілька рядків коду.": Дійсно? Чому б не додати нові поля чи нові методи? Важливий момент - ви лише додаєте (розширюєте), а не змінюєте те, що вже є.
Джорджо

Я думаю, що принцип має сенс при роботі зі стандартними бібліотеками / рамками. Ви не хочете відкривати та змінювати добре встановлені фрагменти коду. В іншому випадку все стосується постійного рефакторингу та тестування, тестування, тестування.
mastaBlasta

@Giorgio Sure, додавання нових полів або методів - це те, що я рекомендував би в більшості випадків. Але це не розширення , це "модифікація"; вся суть OCP полягає в тому, що код повинен бути «закритий для модифікації» (тобто, жодних змін у попередньому вихідному файлі), не будучи «відкритим для розширення»; і розширення в OCP досягається завдяки успадкуванню впровадження.
Rogério

@ Rogério: Чому ви визначаєте межу між розширенням та модифікацією на рівні класу? Чи є певна причина цього? Я вважаю за краще встановити його на рівні методу: зміна методу змінює поведінку вашої програми, додавання (загальнодоступного) методу розширює його інтерфейс.
Джорджіо

6

Особисто я вважаю, що цей принцип слід сприймати з дрібкою солі. Код органічний, бізнес змінюється і змінюється код відповідно до потреб бізнесу з часом.

Мені дуже важко обійтися з приводу того, що абстракція є ключовою. Що робити, якщо абстракція спочатку була неправильною? Що робити, якщо функція бізнесу суттєво змінилася?

Цей принцип по суті забезпечує те, що ОРИГІНАЛЬНІ наміри та поведінка дизайну ніколи не повинні змінюватися. Це, мабуть, працює для тих, хто має публічний API, і їхні клієнти мають труднощі в ногу з новими випусками та деякими іншими кращими справами. Однак якщо компанія володіє ВСЕМ кодом, я оскаржую цей принцип.

Маючи гарне тестове покриття вашого коду, це повинно зробити рефакторинг коду базою. Це означає, що добре помилитися з матеріалами - ваші тести допоможуть надати вам кращий дизайн.

Скажіть, що якщо немає ніяких тестів, то цей принцип є надійним.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.