Другое название: Virtual Constructor (Виртуальный Конструктор).

Назначение: определяет интерфейс для создания объекта, но оставляет подклассам возможность принять решение о том, какой класс инстанцировать.

Задача. Каркасы пользуются абстрактными классами для определения и поддержания отношений между объектами. Кроме того, каркас часто отвечает за создание самих объектов.

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

Поскольку решение о том, какой подкласс класса Document инстанцировать, зависит от приложения, то Application не может знать заранее, что именно понадобится. Этому классу известно лишь, когда нужно инстанцировать новый документ, а не какой документ создать. Возникает дилемма: каркас должен инстанцировать классы, но знает он лишь об абстрактных классах, которые инстанцировать нельзя.

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

 

 

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

Общая структура решения.

 

 

Product (Document) – продукт: определяет интерфейс объектов, создаваемых фабричным методом.

ConcreteProduct (DrawingDocument) – конкретный продукт: реализует интерфейс Product.

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

ConcreteCreator (DrawingApplication) – конкретный создатель: замещает фабричный метод таким образом, что возвращается экземпляр класса ConcreteProduct.

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

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

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

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

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

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

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

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

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

Параметризованные фабричные методы – это еще один вариант паттерна, который позволяет фабричному методу создавать разные виды продуктов. Фабричному методу передается параметр, который идентифицирует вид создаваемого объекта. Все объекты, получающиеся с помощью фабричного метода, разделяют общий интерфейс Product. В примере с документами класс Application может поддерживать разные виды документов. Вы передаете методу CreateDocument лишний параметр, который и определяет, документ какого вида нужно создать.

Cоглашения об именовании: рекомендуется применять такие соглашения об именах, которые дают ясно понять, что вы пользуетесь фабричными методами – это слова «Create», «Make» и т.п. внутри имен соответствующих методов.

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

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

Фабричные методы очень часто вызываются внутри Шаблонных Методов. В примере с документами: NewDocument – это типичный шаблонный метод.