Фабричный метод - паттерн (шаблон) проектирования - описание

Приведём ниже описание паттрена на основе стандартных разделов

Название и классификация

Фабричный метод - паттерн, порождающий классы - относится к порождающим паттернам (шаблонам)

Назначение

Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой именно класс (продукт) инстанцировать.
Фабричный метод позволяет классу делегировать инстанцирование подклассам.

Псевдоним

Паттерн Фабричный метод известен также под именем VirtualConstructor (виртуальный конструктор)

Мотивация

Пусть у нас есть приложение - ну или мы хотим его написать - причём такое что оно может создавать документы разных типов - но мы не знаем заранее какой тип документа (= продукта) выберет пользователь -
и тем не менее механизм создания этих документов - если смотреть "снаружи" выглядит сходно - эта схожесть описывается абстрактными классами, при этом абстрактные классы инстацировать нельзя - то есть нельзя создать объекты этих классов.
Как же быть?

Решение нам предлагает паттерн Фабричный метод, который прячет имя конкретного документа в классе приложения, которое его создаёт.
фабричный метод - мотивация
Как мы видим - здесь предлагается выстраивать иерархию объектов - а точнее - две параллельные иерархии - иерархию продуктов и иерархию создателей этих продуктов.

Кстати это более наглядно демонстрирует такая вот вольная диаграмма:
фабричный метод - иерархии объектов

Применимость

Используйте паттерн фабричный метод, когда:

  1. классу заранее неизвестно, объекты каких классов ему нужно создавать;
  2. класс спроектирован так, чтобы объекты, которые он создает, специфицировались подклассами;
  3. класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и вы планируете локализовать знание о том, какой класс принимает эти обязанности на себя.

Структура

Структуру данного шаблона можно представить в виде следующей диаграммы:
фабричный метод - компоненты схема

Участники

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

Отношения

Создатель «полагается» на свои подклассы (конкретные реализации Creator) в определении фабричного метода, который будет возвращать экземпляр подходящего конкретного продукта.

Результаты

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

Потенциальный недостаток

Потенциальный недостаток фабричного метода состоит в том, что клиентам, возможно, придется создавать подкласс класса Creator для создания лишь одного объекта ConcreteProduct. Порождение подклассов оправдано, если клиенту так или иначе приходится создавать подклассы Creator, в противном случае клиенту придется иметь дело с дополнительным уровнем подклассов.

Ещё два возможных применения паттерна фрабричный метод:

Предоставление подклассам операций-зацепок (hooks)

Создание объектов внутри класса с помощью фабричного метода всегда оказывается более гибким решением, чем непосредственное создание. Фабричный метод создает в подклассах операции зацепки для предоставления расширенной версии
объекта.

В примере с документом класс Document мог бы определить фабричный метод CreateFileDialog, который создает диалоговое окно для выбора файла существующего документа. Подкласс этого класса мог бы определить
специализированное для приложения диалоговое окно, заместив этот фабричный метод. В данном случае фабричный метод не является абстрактным,а содержит разумную реализацию по умолчанию;

Соединения параллельных иерархий

Фабричные методы могут вызываться не только создателем: клиенты тоже могут применять фабричные методы, особенно при наличии параллельных иерархий классов.

Рассмотрим, например, графические фигуры, которыми можно манипулировать интерактивно: растягивать, двигать или вращать с помощью мыши.
Реализация таких взаимодействий с пользователем не всегда простое дело. Часто приходится сохранять и обновлять информацию о текущем состоянии манипуляций. Но это состояние нужно только во время самой манипуляции, поэтому помещать его в объект, представляющий фигуру, не следует. К тому же фигуры ведут себя по

Например, растягивание отрезка может сводиться к изменению положения концевой точки, а растягивание текста к изменению
междустрочных интервалов.

При таких ограничениях лучше использовать отдельный объект манипулятор Manipulator, который реализует взаимодействие и контролирует его текущее состояние. У разных фигур будут разные манипуляторы, являющиеся подклассом Manipulator. Получающаяся иерархия класса Manipulator параллельна(по крайней мере, частично) иерархии класса Figure. Класс Figure предоставляет фабричный метод CreateManipulator, который позволяет клиентам создавать соответствующий фигуре манипулятор. Подклассы Figure замещают этот метод так, чтобы он возвращал подходящий для них подкласс Manipulator. Вместо этого класс Figure может реализовать CreateManipulator так, что он будет возвращать экземпляр классаManipulator по умолчанию, а подклассыFigure могут наследовать
это умолчание.

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

Вот схема:

параллеьные иерархии  - манипулятор - фабричный метод

Итак ещё раз (о результатах):

  1. Фабричные методы избавляют проектировщика от необходимости встраивать в код зависящие от приложения классы
  2. Можно создавать (при желании) расширенные объекты - предоставив конкретным реализациям абстрактного Creator возможно переопределить - в случае необходимости ряд методов (как в примере с файловым диалогом)
  3. Соединения параллельных иерархий

Реализация

В этом разделе следует упомянуть об особенностях реализации, они таковы:

  • Выделяют два принципиальных случая для релазации:
    1. когда класс создатель является абстрактным
    2. когда Creator(создатель) - конкретный ("обычный") класс
    3. ну и всё же встречается смешанный тип - когда абстрактный класс содержит реализацию по-умолчанию
  • параметризованные фабричные методы - данная особенность подразумевает, что вообще говоря - с помощью фабричного метода можно создавать разные виды продуктов в зависимости от переданных параметров
  • различные особенности связанные с конкретным языком реализации

Пример кода

Рассмотрим практическую реализацию паттерна на примере PHP

Известные применения

Фабричные методы где только не встречаются - большинство библиотек и каркасов так или иначе используют паттерн Фабричный метод - в частности библиотека ЕТ++[WGM88]

Родственные паттерны

Абстрактная фабрика часто реализуется с помощью фабричных методов.

Пример в разделе «Мотивация» из описания абстрактной фабрики иллюструет также и паттерн фабричные методы.
Паттерн фабричный метод часто вызывается внутри шаблонных методов.

Прототипы не нуждаются в порождении подклассов от класса Creator. Однако им часто бывает необходима операция Initialize в классе Product.
Creator использует Initialize для для инициализации объекта. Фабричному
методу такая операция не требуется.