CodeLAB
на главную карта сайта обратная связь

Популярные задачи:

#Вычисление значения полинома. (63457 hits)
#Код. (182709 hits)
#Подсветка синтаксиса. (32364 hits)
#Работа с камерой. (36730 hits)
#Добавление истории операций(undo&redo) в компонент. (41035 hits)
#Вращение фигуры в плоскости. (41019 hits)
#Предварительная загрузка изображений. (48189 hits)
#Часики на js. (97412 hits)
#Подключение. (28319 hits)
#Использование компилируемых (prepared) запросов. (31810 hits)
#Шифрование произвольных данных. (330699 hits)
#Динамическое изменение цвета полоски прокрутки в IE5.5 и выше. (31740 hits)
#Динамическая очистка выпадающего списка (select) на javascript. (92725 hits)
#Рисование тора. (36030 hits)
#Вставка новой записи в таблицу БД. (37427 hits)
#Сравнение алгоритмов быстрой сортировки. (75116 hits)
#Древовидные структуры. (58491 hits)
#Синус. (62165 hits)
#Счетчик времени с точностью до микросекунд. (130946 hits)
#Сапер. (54570 hits)


Главная >> Каталог задач >> Паттерны >> Порождающие >>

Фабричный метод (Factory Method)

Aвтор:
Дата:
Просмотров: 162736
реализации(java: 10шт...) +добавить

Имя

«Паттерн
Factory Method»
Фабричный метод – паттерн, порождающий подклассы.

Условия, Задача, Назначение

Задает интерфейс для создания объекта, и оставляет подклассам выбор того, какие в действительности классы инстанцировать (среди «Си»-шников был известен также под названием виртуальный конструктор).

Мотивация

Рассмотрим истоки выделения решения в этот отдельный паттерн. Подумаем о каркасах систем, составляющих их архитектуру.  Каркасы пользуются абстрактными классами для определения и поддержания отношений между объектами. Кроме того, каркас часто отвечает за создание самих объектов.
Рассмотрим каркас для приложений, способных представлять пользователю сразу несколько документов. Две основных абстракции в таком каркасе – это классы Application и Document. Оба класса абстрактные, поэтому клиенты должны порождать от них подклассы для создания специфичных для приложения реализаций. Например, чтобы создать приложение для рисования, мы определим классы DrawingApplication и DrawingDocument. Класс Application отвечает за управление документами и создает их по мере необходимости, допустим, когда пользователь выбирает из меню пункт Open (открыть) или New (создать).
Поскольку решение о том, какой подкласс класса Document инстанцировать, зависит от приложения, то Application не может «предсказать», что именно понадобится, в смысле, какой конкретный класс нужно будет инстанцировать. Этому участнику известно лишь, когда нужно инстанцировать новый документ, а не какой документ создать. Возникает дилемма: каркас должен инстанцировать классы, но «знает» он лишь об абстрактных классах, которые инстанцировать нельзя.

Решение предлагает паттерн фабричный метод. В нем инкапсулируется информация о том, какой подкласс класса Document создать, и это знание выводится за пределы каркаса.

Подклассы класса Application переопределяют абстрактную операцию CreateDocument таким образом, чтобы она возвращала подходящий подкласс класса Document. Как только подкласс Application инстанцирован, он может инстанцировать специфические для приложения документы, ничего не зная об их классах. Операцию CreateDocument мы называем фабричным методом, поскольку она отвечает за «изготовление» объекта.

 

Признаки применения, использования паттерна Фабричный метод (Factory Method)

Если:
  1. Классу заранее неизвестно, объекты каких классов ему нужно создавать, поскольку предполагается много различных вариантов функционирования.
  2. класс спроектирован так, чтобы объекты, которые он создает, определялись(специфицировались) только уже в подклассе.
  3. Класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и вы планируете локализовать знание о том, какой класс принимает эти обязанности на себя. Т.е. функциональность логически разделена между главным классом и вспомогательными, каждый из которых выполняет свою конкретную небольшую функцию. И вот здесь требуется гибкость, чтобы позволить легко заменять эти вспомогательные классы.

Решение

Участники паттерна Фабричный метод (Factory Method)

  1. Product(Document) – продукт, абстрактный класс.
    Определяет структуру, интерфейс объектов, создаваемых фабричным методом.
  2. ConcreteProduct(MyDocument) – конкретный продукт, класс.
    Реализует интерфейс Product, т.е. представляет один из вариантов реализации всех действий, возложенных на сущность Product.
  3. Creator(Application) – создатель(«главный класс»), здесь он пока абстрактный.
    Объявляет фабричный метод, возвращающий объект типа Product.
    Creator может также определять реализацию по-умолчанию фабричного метода,  который возвращает объект ConcreteProduct.
    Может вызывать фабричный метод для создания объекта Product.
  4. ConcreteCreator(MyApplication) – продукт, абстрактный класс.
    Определяет структуру, интерфейс объектов, создаваемых фабричным метод.
 

Схема использования паттерна Фабричный метод (Factory Method)

Ответственность за то, какие реально классы будут фигурировать в приложении – создатель назначает подклассам.

Таким образом в каркасе приложения фигурируют только класса Product и Creator. С помощью них выполняется какой-то общий алгоритм. Дальше – в уже клиентской части приложения, использующей этот каркас, на каждом этапе отдельно решается какой конкретно подкласс ConcreteProduct и ConcreteCreator инстанцируется, после чего запускается на выполнение этот общий алгоритм определенный каркасом, который уже используя эти подклассы получает на выходе конкретный вариант результатов функционирования.

Вопросы, касающиеся реализации паттерна Фабричный метод (Factory Method)

Некоторые аспекты, возникающие при использовании данного паттерна:
  1. 2 основных разновидности паттерна.
    Во-первых, это случай, когда класс Сreator является абстрактным и не содержит реализации объявленного в нем фабричного метода. Вторая возможность: Creator – конкретный класс, в котором по умолчанию есть реализация фабричного метода.
    Редко, но встречается и абстрактный класс, имеющий реализацию по умолчанию; В первом случае для определения реализации необходимы подклассы, поскольку никакого разумного умолчания не существует. При этом обходится проблема, связанная с необходимостью инстанцировать заранее неизвестные классы.
    Во-втором случае конкретный класс Creator использует фабричный метод, главным образом ради повышения гибкости. Выполняется правило: «Создавай объекты в отдельной операции, чтобы подклассы могли подменить способ их создания». Соблюдение этого правила гарантирует, что авторы подклассов смогут при необходимости изменить класс объектов, инстанцируемых их родителем.
  2. Параметризованные фабричные методы.
    Это еще один вариант паттерна, который позволяет фабричному методу создавать разные виды продуктов.
    Фабричному методу передается параметр, который идентифицирует вид создаваемого объекта. Все объекты, получающиеся с помощью фабричного метода, разделяют общий интерфейс Product. В примере с документами класс Application может поддерживать разные виды документов. Вы передаете методу CreateDocument лишний параметр, который и определяет, документ какого вида нужно создать.
    Параметризованный фабричный метод в общем случае имеет следующий вид (здесь My Product и Your Product - подклассы Product):
     Параметризованный фабричный метод [C++]  ссылка
    1. class Creator {
    2. public:
    3. virtual Product* Create(Productld);
    4. };
    5.  
    6. Product* Creator::Create (Productld id) {
    7. if (id == MINE) return new MyProduct;
    8. if (id == YOURS) return new YourProduct;
    9. // выполнить для всех остальных продуктов...
    10. return 0;
    11. }

    Замещение параметризованного фабричного метода позволяет легко и избирательно расширить или заменить продукты, которые изготавливает создатель.
    Можно завести новые идентификаторы для новых видов продуктов или ассоциировать существующие идентификаторы с другими продуктами.
    Например, подкласс MyCreator мог бы переставить местами MyProduct и YourProduct для поддержки третьего подкласса Their Product:
     Замещение параметризованного фабричного метода [C++]  ссылка
    1. Product* MyCreator::Create (Productld id) {
    2. if (id == YOURS) return new MyProduct;
    3. if (id == MINE) return new YourProduct;
    4. // ! YOURS и MINE переставлены
    5. if (id == THEIRS) return new TheirProduct;
    6. // вызывается, если больше ничего не осталось
    7. return Creator::Create(id);
    8. }

  3. Использование шаблонов, чтобы не порождать подклассы.
    К сожалению, допустима ситуация, когда вам придется порождать подклассы только для того, чтобы создать подходящие объекты-продукты. В некоторых языках этого можно избежать, предоставив шаблонный подкласс класса Creator, параметризованный классом Product:
     Использование шаблонов, чтобы не порождать подклассы [C++]  ссылка
    1. class Creator {
    2. public :
    3. virtual Product* CreateProduct () = 0;
    4. };
    5.  
    6. template <class TheProduct>
    7. class StandardCreator: public Creator {
    8. public:
    9. virtual Product* CreateProduct();
    10. };
    11. template <class TheProduct>
    12. Product* StandardCreator<TheProduct>::CreateProduct () {
    13. return new TheProduct;
    14. }
    15.  
    16. /* С помощью данного шаблона клиент передает только класс продукта, по-
    17. рождать подклассы от Creator не требуется: */
    18. class MyProduct : public Product {
    19. public:
    20. MyProduct();
    21. //...
    22. };
    23. StandardCreator<MyProduct> myCreator;

Результаты

Фабричные методы избавляют проектировщика от необходимости встраивать в код зависящие от приложения классы. Код имеет дело только с интерфейсом абстрактного класса Product, поэтому впоследствии он сможет работать с любыми определенными пользователями классами конкретных продуктов.
Потенциальный недостаток фабричного метода состоит в том, что клиентам, возможно, придется создавать подкласс класса Creator для создания одного любого нового объекта ConcreteProduct. Порождение подклассов оправдано, если клиенту так или иначе приходится создавать подклассы Creator, в противном случае клиенту придется иметь дело с дополнительным уровнем подклассов.
Кроме того можно выделить еще 2 последствия применения паттерна фабричный метод:
  1. Возможность расширения дефолтных действий.
    Создание объектов внутри класса с помощью фабричного метода всегда оказывается более гибким решением, чем непосредственное создание. Фабричный метод дает возможность подклассам наследовать реализацию каких-то действий по-умалчанию.
    В примере с документом класс Document мог бы определить конкретный(не абстрактный) фабричный метод CreateFileDialog, который создает диалоговое окно для выбора файла существующего документа. Подкласс этого класса мог бы определить специализированное для приложения диалоговое окно, заместив этот фабричный метод. В данном случае фабричный метод не является абстрактным, а содержит разумную реализацию по умолчанию.
  2. Соединяет параллельные иерархии.
    В примерах, которые мы рассматривали до сих пор, фабричные методы вызывались только создателем. Но это совершенно необязательно: клиенты тоже могут применять фабричные методы, особенно при наличии параллельных иерархий классов.
    Параллельные иерархии возникают в случае, когда класс делегирует часть своих обязанностей другому классу, не являющемуся производным от него. Рассмотрим, например, графические фигуры, которыми можно манипулировать интерактивно: растягивать, двигать или вращать с помощью мыши.
    Реализация таких взаимодействий с пользователем - не всегда простое дело. Часто приходится сохранять и обновлять информацию о текущем состоянии манипуляций. Но это состояние нужно только во время самой манипуляции, поэтому помещать его в объект, представляющий фигуру, не следует. К тому же фигуры ведут себя по-разному, когда пользователь манипулирует ими. Например, растягивание отрезка может сводиться к изменению положения концевой точки, а растягивание текста - к изменению междустрочных интервалов.
    При таких ограничениях лучше использовать отдельный объект-манипулятор Manipulator, который реализует взаимодействие и контролирует его текущее состояние. У разных фигур будут разные манипуляторы, являющиеся подклассом Manipulator. Получающаяся иерархия класса Manipulator параллельна (по крайней мере, частично) иерархии класса Figure.
    Класс Figure предоставляет фабричный метод CreateManipulator, который позволяет клиентам создавать соответствующий фигуре манипулятор. Подклассы Figure замещают этот метод так, чтобы он возвращал подходящий для них подкласс Manipulator. Вместо этого класс Figure может реализовать CreateManipulator так, что он будет возвращать экземпляр класса Manipulator по умолчанию, а подклассы Figure могут наследовать это умолчание. Те классы фигур, которые функционируют по описанному принципу, не нуждаются в специальном манипуляторе, поэтому иерархии параллельны только отчасти.
    Обратите внимание, как фабричный метод определяет связь между обеими иерархиями классов. В нем локализуется знание о том, какие классы способны работать совместно. 

 

Пример

Требуется автоматизировать процесс сборки компьютера из комплектующих. Т.к. каждая новая конфигурация имеет детали разных производителей – уместно сделать единый общий абстрактный класс (Creator) с различными фабричными методами, получающими отдельные детали (Product-ы) и соответственно одна общая функция собирающая все вместе: ComputerCreator.java

Каждый фабричный метод возвращает одну из комплектующих. Класс ComputerCreator предоставляет реализации по умолчанию, которые возвращают простейшие варианты процессора, монитора, клавиатуры: Monitor.java, Processor.java, KeyBoard.java.

Далее для каждой конфигурации создаем уже свои, конкретные классы (ConcreteCreator/ConcreteProduct). Т.к. в результате нам нужен пентиум с расширенной клавиатурой, то все что там нужно будет сделать, учитывая, что сборку монитора можно оставить прежней это 3 класса, 1 Creator и 2 новых продукта: PentiumCreator.java, KeyBoardFull.java, Processor64.java.

Ну а если завтра нам понадобится более экономичный вариант AMD с ЖК дисплеем, мы осуществим это через AMDCreator: AMDCreator.java, AMDSempron.java, LSDMonitor.java.

Обратите внимание, что опять же, так фабричные методы не абстрактны мы имели замечательную возможность не дублировать новый метод создания клавиатуры, а наследовать уже имеющийся, потому что этот вариант по-умалчанию нас вполне устраивает.

Реализации:

java(10)   +добавить

1) ComputerCreator.java на java, code #420[автор:this]
2) Monitor.java на java, code #421[автор:this]
3) Processor.java на java, code #422[автор:this]
4) KeyBoard.java на java, code #423[автор:this]
5) PentiumCreator.java на java, code #425[автор:this]
6) KeyBoardFull.java на java, code #426[автор:this]
7) Processor64.java на java, code #427[автор:this]
8) AMDCreator.java на java, code #428[автор:this]
9) AMDSempron.java на java, code #429[автор:this]
10) LSDMonitor.java на java, code #430[автор:this]