Чому цей ShapeFactory використовує умовні висловлювання, щоб визначити, який об'єкт інстанціювати. Чи не потрібно нам змінювати ShapeFactory, якщо ми хочемо додати інші класи в майбутньому? Чому це не порушує принципу відкритого закритого типу?
Чому цей ShapeFactory використовує умовні висловлювання, щоб визначити, який об'єкт інстанціювати. Чи не потрібно нам змінювати ShapeFactory, якщо ми хочемо додати інші класи в майбутньому? Чому це не порушує принципу відкритого закритого типу?
Відповіді:
Загальноприйнята об'єктно-орієнтована мудрість полягає у тому, щоб уникати if
висловлювань та замінювати їх динамічною розсилкою перекритих методів у підкласах абстрактного класу. Все йде нормально.
Але суть фабричного зразка полягає в тому, щоб позбавити вас від необхідності знати про окремі підкласи та працювати лише з абстрактним суперкласом . Ідея полягає в тому, що фабрика краще за вас знає, який саме клас інстанціювати, і вам буде краще працювати лише з методами, опублікованими суперкласом. Це часто правда і цінна закономірність.
Тому немає можливості, щоб написання фабричного класу могло відмовитися від if
тверджень. Це перекладе тягар вибору конкретного класу на абонента, саме цього слід уникати. Не всі принципи є абсолютними (насправді жоден принцип не є абсолютним), і якщо ви використовуєте цю схему, ви вважаєте, що вигода від неї більша, ніж користь від не використання if
.
if
s. Дивіться відповідь @ BЈовић для простого прикладу, як цього досягти. Захищений.
У прикладі, ймовірно, використовується умовний вислів, оскільки він найпростіший. Більш складна реалізація може використовувати карту чи конфігурацію або (якщо ви хочете по-справжньому фантазії) якийсь реєстр, де класи можуть зареєструватися самостійно. Однак немає нічого поганого в використанні умовного, якщо кількість класів невелика і змінюється нечасто.
Розширення умовного додавання підтримки нового підкласу в майбутньому справді суворо кажучи було б порушенням принципу відкритого / закритого типу. "Правильним" рішенням було б створити нову фабрику з тим же інтерфейсом. З огляду на це, дотримання принципу O / C завжди повинно суперечити іншим принципам дизайну, таким як KISS та YAGNI.
Однак, показаний код є чітко прикладом коду, який призначений для відображення концепції фабрики та нічого іншого. Наприклад, це справді поганий стиль повернення нуля, як це робиться в прикладі, але більш детальна робота з помилками просто затьмарить суть. Приклад коду - це не якість якості виробництва, будь-який ви не повинні очікувати, що він буде.
Сама схема не порушує принципу відкритого / закритого доступу (OCP). Однак ми порушуємо OCP, коли використовуємо шаблон неправильно.
Проста відповідь на це питання полягає в наступному:
У наведеному прикладі ваш базовий функціонал підтримує три форми: Коло, Прямокутник і Квадрат. Припустимо, вам потрібно підтримувати трикутник, п’ятикутник і шестикутник у майбутньому. Для цього БЕЗ порушення OCP, ви повинні створити додаткову фабрику для підтримки нових форм (назвемо AdvancedShapeFactory
), а потім скористатися AbstractFactory, щоб вирішити, яку фабрику потрібно створити, щоб створити будь-які фігури, які вам потрібні.
Якщо ви говорите про абстрактний заводський зразок, прийняття рішень часто відбувається не в самій Фабриці, а в коді програми. Це той код, який вибирає, який конкретний завод інстанціювати і передавати клієнтському коду, який буде використовувати об'єкти, виготовлені Фабрикою. Дивіться кінець прикладу Java тут: https://en.wikipedia.org/wiki/Ab абстракт_factory_pattern
Прийняття рішень не обов'язково означає if
твердження. Він може читати конкретний заводський тип з конфігураційного файлу, виводити його зі структури карти тощо.
Якщо ви думаєте про Open-Close на рівні класу на цій фабриці, ви створюєте інший клас у вашій системі Open-Close, наприклад, якщо у вас є інший клас, який приймає одну форму і обчислює площу (типовий приклад), цей клас є OpenClose, оскільки він може обчислити площу для нових типів фігур без змін. Тоді у вас є ще один клас, який малює фігуру, ще один клас, який приймає N фігур і повертає більший, і ви можете взагалі подумати, що інші класи у вашій системі, що займається фігурами, є Open-Close (принаймні, про форми). Дивлячись на дизайн, фабрика дає можливість решті системи бути відкритою-закритою, а сама фабрика НЕ ВІДКРИТТЯ.
Звичайно, ви можете зробити цю фабрику відкритими-закритими, завдяки якомусь динамічному завантаженню, і вся ваша система може бути Open-Close (ви можете додати нові форми, скидаючи якусь банку на classpath, наприклад). Вам потрібно оцінити, чи варто ця додаткова складність залежно від системи, яку ви будуєте, не всі системи потребують підключуваних функцій, і не всі системи повинні бути повністю відкритими-закритими.
Принцип відкритого закриття, як принцип заміщення Ліскова, застосовується до дерев класів, до ієрархій успадкування. У вашому прикладі фабричний клас відсутній у родинному дереві класів, які він створює, тому він не може порушувати ці правила. Було б порушення, якщо ваш GetShape (або більш влучно названий CreateShape) був реалізований в базовому класі Shape.
Все залежить від того, як ви це реалізуєте. Ви можете використовувати std::map
для утримання покажчиків функцій для функцій, що створюють об'єкти. Тоді принцип відкритого / закритого не порушується. Або перемикач / чохол.
У будь-якому випадку, якщо вам не подобається заводський зразок, ви завжди можете використовувати ін'єкцію залежності.