Чи порушує заводський зразок відкритий / закритий принцип?


14

Чому цей ShapeFactory використовує умовні висловлювання, щоб визначити, який об'єкт інстанціювати. Чи не потрібно нам змінювати ShapeFactory, якщо ми хочемо додати інші класи в майбутньому? Чому це не порушує принципу відкритого закритого типу?

Заводський дизайн шаблону

ShapeFactory Дизайн


2
Який «заводський зразок» ви конкретно посилаєтесь? Загалом, фабрика - це будь-який об'єкт або метод, який служить для інстанції об'єкта. Потім існують конкретні варіації цієї загальної ідеї, такі як абстрактний заводський візерунок, де кожен фабричний екземпляр представляє певну палітру вибору - як правило, керується за допомогою підкласингу, а не умовних умов.
amon


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

@ArmonSafai: Ви багато пов'язуєте цю публікацію в блозі, але не дуже пояснюєте, чому. Ми всі якось невідомі до шаблону? У нас теж є Google, як і ви.
Роберт Харві

1
@RobertHarvey Я пов'язую це повідомлення в блозі, щоб показати, як заводський зразок на цій сторінці використовує умовні умови
Armon Safai

Відповіді:


20

Загальноприйнята об'єктно-орієнтована мудрість полягає у тому, щоб уникати ifвисловлювань та замінювати їх динамічною розсилкою перекритих методів у підкласах абстрактного класу. Все йде нормально.

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

Тому немає можливості, щоб написання фабричного класу могло відмовитися від ifтверджень. Це перекладе тягар вибору конкретного класу на абонента, саме цього слід уникати. Не всі принципи є абсолютними (насправді жоден принцип не є абсолютним), і якщо ви використовуєте цю схему, ви вважаєте, що вигода від неї більша, ніж користь від не використання if.


2
Цілком можливо створити заводський візерунок без великої кількості ifs. Дивіться відповідь @ BЈовић для простого прикладу, як цього досягти. Захищений.
Девід Арно


11
@DavidArno Звичайно, існують різні способи вибору конкретного класу. Локатор обслуговування - це один, конфігурація IoC-контейнера - інша. Це лише деталі реалізації; вони не відволікають від головного повідомлення Кілліана, що фабрика позбавляє абонента від того, щоб вирішити, який конкретний клас для інстанції. Не зациклюйтеся на деталях.
Роберт Харві

1
Страхітливе твердження, яке жодним чином не відповідає на питання.
Мартін

1
@ R.Schmitz Я думаю, що ви неправі в своїх припущеннях. Я думаю, що багато людей пропустили це запитання з ОП: "Чи не потрібно нам змінювати ShapeFactory, якщо ми хочемо додати інші класи в майбутньому?" ЧИСТО, ОП розгублено, думаючи, що ця модель порушує OCP, оскільки для додання нової функціональності вам доведеться змінити існуючий код. Правильну відповідь на це питання можна знайти в моїй відповіді. Коротка відповідь: Ви залишаєте цей код у спокої, і ви застосовуєте абстрактний заводський візерунок, щоб розширити (не змінити) існуючу функціональність. Через це відповідь Кіліана НЕ стосується питання.
hfontanez

5

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

Розширення умовного додавання підтримки нового підкласу в майбутньому справді суворо кажучи було б порушенням принципу відкритого / закритого типу. "Правильним" рішенням було б створити нову фабрику з тим же інтерфейсом. З огляду на це, дотримання принципу O / C завжди повинно суперечити іншим принципам дизайну, таким як KISS та YAGNI.

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


Чи можете ви пояснити, як працюватиме карта / конфігурація / реєстр?
Армон Сафай


Фабрики самореєстрації, AFAIK, неможливі в C ++ всередині статичних бібліотек, оскільки невикористані (тобто, використовувані odr) глобальні змінні відкидаються ланцюжками інструментів.
void.pointer

@ArmonSafai прочитав це для більшого розуміння goo.gl/RYuNSM
AZ_

2

Сама схема не порушує принципу відкритого / закритого доступу (OCP). Однак ми порушуємо OCP, коли використовуємо шаблон неправильно.

Проста відповідь на це питання полягає в наступному:

  1. Створіть свою базову функціональність за допомогою заводського методу .
  2. Розширюйте свою функціональність, використовуючи абстрактний заводський візерунок

У наведеному прикладі ваш базовий функціонал підтримує три форми: Коло, Прямокутник і Квадрат. Припустимо, вам потрібно підтримувати трикутник, п’ятикутник і шестикутник у майбутньому. Для цього БЕЗ порушення OCP, ви повинні створити додаткову фабрику для підтримки нових форм (назвемо AdvancedShapeFactory), а потім скористатися AbstractFactory, щоб вирішити, яку фабрику потрібно створити, щоб створити будь-які фігури, які вам потрібні.


Я дуже віддаю перевагу самореєстраційним рішенням фабрик (за відсутності справжнього конфігуруваного контейнера IoC, який найкраще), оскільки в іншому випадку те, що ми отримуємо з вашої пропозиції, - це в основному фабрики фабрик , і саме тоді справи стають просто надмірно складними.
Nom1fan

1

Якщо ви говорите про абстрактний заводський зразок, прийняття рішень часто відбувається не в самій Фабриці, а в коді програми. Це той код, який вибирає, який конкретний завод інстанціювати і передавати клієнтському коду, який буде використовувати об'єкти, виготовлені Фабрикою. Дивіться кінець прикладу Java тут: https://en.wikipedia.org/wiki/Ab абстракт_factory_pattern

Прийняття рішень не обов'язково означає ifтвердження. Він може читати конкретний заводський тип з конфігураційного файлу, виводити його зі структури карти тощо.



Якщо я, абонент, приймаю рішення, який конкретний клас інстанціювати, то чому я морочусь з «Абстрактним заводом»?
Роберт Харві

Будь ласка, визначте "абонент". Як я описую у своїй відповіді, є глобальний код програми, а потім - код, який потрібно нерестувати об'єкти за допомогою Factory. Хоча останні дійсно потрібно не знати конкретний клас для створення екземпляра, інший контекстний код повинен знати про це , і новому його вгору ...
guillaume31

0

Якщо ви думаєте про Open-Close на рівні класу на цій фабриці, ви створюєте інший клас у вашій системі Open-Close, наприклад, якщо у вас є інший клас, який приймає одну форму і обчислює площу (типовий приклад), цей клас є OpenClose, оскільки він може обчислити площу для нових типів фігур без змін. Тоді у вас є ще один клас, який малює фігуру, ще один клас, який приймає N фігур і повертає більший, і ви можете взагалі подумати, що інші класи у вашій системі, що займається фігурами, є Open-Close (принаймні, про форми). Дивлячись на дизайн, фабрика дає можливість решті системи бути відкритою-закритою, а сама фабрика НЕ ​​ВІДКРИТТЯ.

Звичайно, ви можете зробити цю фабрику відкритими-закритими, завдяки якомусь динамічному завантаженню, і вся ваша система може бути Open-Close (ви можете додати нові форми, скидаючи якусь банку на classpath, наприклад). Вам потрібно оцінити, чи варто ця додаткова складність залежно від системи, яку ви будуєте, не всі системи потребують підключуваних функцій, і не всі системи повинні бути повністю відкритими-закритими.


0

Принцип відкритого закриття, як принцип заміщення Ліскова, застосовується до дерев класів, до ієрархій успадкування. У вашому прикладі фабричний клас відсутній у родинному дереві класів, які він створює, тому він не може порушувати ці правила. Було б порушення, якщо ваш GetShape (або більш влучно названий CreateShape) був реалізований в базовому класі Shape.


-2

Все залежить від того, як ви це реалізуєте. Ви можете використовувати std::mapдля утримання покажчиків функцій для функцій, що створюють об'єкти. Тоді принцип відкритого / закритого не порушується. Або перемикач / чохол.

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


6
Наскільки перемикач / корпус краще, ніж умовні? Використовувати карту / dict / table для представлення коду як даних добре, якщо вам потрібен реєстр різних реалізацій - наприклад, у деяких реалізаціях контейнерів DI. Але мати різні зворотні виклики одного типу не потрібно для більшості заводів! Я не зовсім розумію, чому ви це пропонуєте. Крім того, багато контейнерів DI реалізовані з точки зору заводських об'єктів, тому пропозиція використовувати DI замість заводів здається трохи круговою.
амон

1
@amon Я мав на увазі використовувати інші типи DI - не заводські.
BЈоviћ


1
Як ваша фабрика вирішить, який покажчик використовувати? Зрештою ви повинні прийняти рішення.
whatsisname

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