Метафора
Метафора паттерна описывается его названием. Когда вам необходима бутылка колы, вам не нужно при этом знать, как их производят на фабрике. Достаточно сказать «дайте мне колы», а фабрика уже возвращает вам бутылку. Каким образом создать эту бутылку остается полностью на совести фабрики. Предназначение фабрики в том, чтобы можно было бы в любой момент времени полностью изменить процесс создания продукта так, чтобы клиент ничего об этом не знал и запрашивал бы продукт, как и раньше. Пример: клиент хочет прохладительный напиток, при этом снабдив клиента одной или другой фабрикой, мы сможем переключать его потребность на бутылку колы или спрайта.
Как правило, одна фабрика занимается «производством» только одного рода «продуктов». Не рекомендуется «фабрику колы» создавать с учетом производства автомобильных покрышек. Как и в жизни, паттерн «фабрика» часто создается «одиночкой».
Назначение
Паттерн применяется, когда необходимо получать семейство взаимосвязанных или родственных объектов не специфицируя их конкретных классов. Таким образом, в интерфейсах фабрик не указываются конкретные классы, вместо этого указываются абстрактные классы, из которых уже наследуются конкретные, каждая фабрика решает какой конкретный класс из семейства необходимо ей создать.
Паттерн не рекомендуется использовать:
- Когда вектор изменения направлен на добавление новых продуктов, т.к. добавляя один метод для создания некоторого продукта, вам необходимо продублировать его на все фабрики;
- Для создания не связанных объектов: бутылка колы и автомобиль, т.к. это усложняет понимание необходимости в такой фабрике.
Плюсы:
- Фабрика гарантирует сочетаемость продуктов, так в бутылке из-под coca-cola вы не получите машинное масло (тут бутылка колы рассматривается как набор отдельных объектов: пластиковый контейнер и его наполнение);
- Упрощение взаимозаменяемости семейства продуктов, Вы можете подменить одну фабрику на другую, при этом клиент будет оперировать новым семейством продуктов, т.к. внутри используются абстрактные интерфейсы. Конкретные классы изолируются внутри фабрик, клиенту о них знать не нужно;
- Возможность расширить систему на новые фабрики не нарушая OCP принцип (SOLID).
Диаграмма
На данном рисунке мы видим некоторый класс Client, который работает с абстрактной фабрикой. Все конкретные фабрики наследуются от абстрактной фабрики (AbstractFactory), абстрактная фабрика создает некие абстрактные продукты. Конкретные фабрики решают, какие конкретные продукты они будут создавать. Клиент во время исполнения снабжается той или иной реализацией абстрактной фабрики.
Пример
В данном примере и далее по ходу статьи нет отдельного класса клиента, вся работа с шаблонами происходит непосредственно в программе:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
*&---------------------------------------------------------------------* *& Описание абстракций *&---------------------------------------------------------------------* CLASS lcl_abstract_product_a DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: run ABSTRACT. ENDCLASS. CLASS lcl_abstract_product_b DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: run ABSTRACT. ENDCLASS. CLASS lcl_abstract_factory DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: create_product_a ABSTRACT RETURNING VALUE(ro_product_a) TYPE REF TO lcl_abstract_product_a, create_product_b ABSTRACT RETURNING VALUE(ro_product_b) TYPE REF TO lcl_abstract_product_b. ENDCLASS. *&---------------------------------------------------------------------* *& Описание конкретных реализаций - продуктов *&---------------------------------------------------------------------* CLASS lcl_concrete_product_a_1 DEFINITION INHERITING FROM lcl_abstract_product_a. PUBLIC SECTION. METHODS run REDEFINITION. ENDCLASS. CLASS lcl_concrete_product_a_2 DEFINITION INHERITING FROM lcl_abstract_product_a. PUBLIC SECTION. METHODS run REDEFINITION. ENDCLASS. CLASS lcl_concrete_product_b_1 DEFINITION INHERITING FROM lcl_abstract_product_b. PUBLIC SECTION. METHODS run REDEFINITION. ENDCLASS. CLASS lcl_concrete_product_b_2 DEFINITION INHERITING FROM lcl_abstract_product_b. PUBLIC SECTION. METHODS run REDEFINITION. ENDCLASS. *&---------------------------------------------------------------------* *& Описание конкретных реализаций - фабрик *&---------------------------------------------------------------------* CLASS lcl_concrete_factory_1 DEFINITION INHERITING FROM lcl_abstract_factory. PUBLIC SECTION. METHODS: create_product_a REDEFINITION, create_product_b REDEFINITION. ENDCLASS. CLASS lcl_concrete_factory_2 DEFINITION INHERITING FROM lcl_abstract_factory. PUBLIC SECTION. METHODS: create_product_a REDEFINITION, create_product_b REDEFINITION. ENDCLASS. *&---------------------------------------------------------------------* *& Реализация конкретных продуктов *&---------------------------------------------------------------------* CLASS lcl_concrete_product_a_1 IMPLEMENTATION. METHOD run. WRITE: / 'Run with product A-1'. ENDMETHOD. ENDCLASS. CLASS lcl_concrete_product_a_2 IMPLEMENTATION. METHOD run. WRITE: / 'Run with product A-2'. ENDMETHOD. ENDCLASS. CLASS lcl_concrete_product_b_1 IMPLEMENTATION. METHOD run. WRITE: / 'Run with product B-1'. ENDMETHOD. ENDCLASS. CLASS lcl_concrete_product_b_2 IMPLEMENTATION. METHOD run. WRITE: / 'Run with product B-2'. ENDMETHOD. ENDCLASS. *&---------------------------------------------------------------------* *& Реализация конкретных фабрик *&---------------------------------------------------------------------* CLASS lcl_concrete_factory_1 IMPLEMENTATION. METHOD create_product_a. CREATE OBJECT ro_product_a TYPE lcl_concrete_product_a_1. ENDMETHOD. METHOD create_product_b. CREATE OBJECT ro_product_b TYPE lcl_concrete_product_b_1. ENDMETHOD. ENDCLASS. CLASS lcl_concrete_factory_2 IMPLEMENTATION. METHOD create_product_a. CREATE OBJECT ro_product_a TYPE lcl_concrete_product_a_2. ENDMETHOD. METHOD create_product_b. CREATE OBJECT ro_product_b TYPE lcl_concrete_product_b_2. ENDMETHOD. ENDCLASS. *&---------------------------------------------------------------------* *& Работа с фабриками *&---------------------------------------------------------------------* START-OF-SELECTION. DATA: lo_factory TYPE REF TO lcl_abstract_factory, lo_product_a TYPE REF TO lcl_abstract_product_a, lo_product_b TYPE REF TO lcl_abstract_product_b. CREATE OBJECT lo_factory TYPE lcl_concrete_factory_1. lo_product_a = lo_factory->create_product_a( ). lo_product_b = lo_factory->create_product_b( ). lo_product_a->run( ). lo_product_b->run( ). CREATE OBJECT lo_factory TYPE lcl_concrete_factory_2. lo_product_a = lo_factory->create_product_a( ). lo_product_b = lo_factory->create_product_b( ). lo_product_a->run( ). lo_product_b->run( ). |
В нашем примере фабрика создается непосредственно в коде программы, однако в реальных программах заранее определить нужную фабрику может быть невозможно. Решается это за счёт динамического создания фабрики, класс которой определен из настроек, где-нибудь в кластере ракурсов, при этом настройка эта считывается в еще одном классе, который служит своего рода фабрикой фабрик.
Видео
На zevolving приведен неплохой пример задачи для использования абстр. фабрики: http://zevolving.com/2011/11/abap-objects-design-patterns-%E2%80%93-abstract-factory/
Из личной практики использования абстрактной фабрики: работа с различными видами карточек DMS.
Ссылка изменилась:
http://zevolving.com/abap-objects-design-patterns-%e2%80%93-abstract-factory/