В объектно-ориентированном программировании шаблон фабричного метода — это шаблон проектирования , который использует фабричные методы для решения проблемы создания объектов без указания их точного класса . Вместо вызова конструктора это делается путем вызова фабричного метода для создания объекта. Фабричные методы могут быть либо указаны в интерфейсе и реализованы дочерними классами, либо реализованы в базовом классе и при необходимости переопределены производными классами . Это один из 23 классических шаблонов проектирования, описанных в книге « Шаблоны проектирования» (часто называемых «Бандой четырех» или просто «GoF»), который относится к подкатегории « творческий шаблон» . [1]
Шаблон проектирования «Фабричный метод» решает такие проблемы, как:
Шаблон проектирования «Фабричный метод» описывает, как решать такие проблемы:
Это позволяет писать подклассы, которые могут изменить способ создания объекта (например, путем переопределения класса для создания экземпляра).
См. также диаграмму классов UML ниже.
«Определите интерфейс для создания объекта, но позвольте подклассам решать, какой класс создавать. Метод Factory позволяет классу отложить создание экземпляра, который он использует, до подклассов». ( Банда из четырех )
Создание объекта часто требует сложных процессов, которые нецелесообразно включать в составной объект. Создание объекта может привести к значительному дублированию кода, может потребовать информацию, недоступную для составного объекта, может не обеспечить достаточный уровень абстракции или иным образом может не входить в состав составного объекта . Шаблон проектирования фабричного метода решает эти проблемы, определяя отдельный метод для создания объектов, подклассы которого затем могут переопределить, чтобы указать производный тип продукта, который будет создан.
Шаблон фабричного метода опирается на наследование, поскольку создание объектов делегируется подклассам, которые реализуют фабричный метод для создания объектов. [2] Как показано в примере C# ниже, шаблон фабричного метода также может опираться на интерфейс — в данном случае IPerson — для реализации.
На приведенной выше диаграмме классов UML класс , которому требуется объект, не создает экземпляр класса напрямую. Вместо этого ссылается на отдельный объект для создания объекта продукта, что делает независимость от того, какой конкретный класс создается. Подклассы могут переопределить, какой класс создавать. В этом примере подкласс реализует абстрактное, создавая экземпляр класса.Creator
Product
Product1
Creator
factoryMethod()
Creator
Creator
Creator1
factoryMethod()
Product1
Эта реализация C++14 основана на реализации до C++98, описанной в книге. [4]
#include <iostream> #include <память> enum ProductId { МОЙ , ВАШ }; // определяет интерфейс объектов, создаваемых фабричным методом. класс Product { public : virtual void print () = 0 ; виртуальный ~ Продукт () = по умолчанию ; }; // реализует интерфейс продукта. класс ConcreteProductMINE : public Product { public : void print () { std :: cout << "this=" << this << " print MINE \n " ; } }; // реализует интерфейс продукта. класс ConcreteProduct YOURS : public Product { public : void print () { std :: cout << "this=" << this << " распечатайте ВАШЕ \n " ; } }; // объявляет фабричный метод, который возвращает объект типа Product. class Creator { public : virtual std :: unique_ptr <Product> create ( ProductId id ) { if ( ProductId :: MINE == id ) return std :: make_unique < ConcreteProductMINE > ( ) ; if ( ProductId :: YOURS == id ) return std :: make_unique < ConcreteProduct YOURS > (); // повторяем для остальных продуктов... вернуть нульптр ; } виртуальный ~ Создатель () = по умолчанию ; }; int main () { // unique_ptr предотвращает утечку памяти. std :: unique_ptr < Создатель > создатель = std :: make_unique < Создатель > (); std :: unique_ptr < Продукт > продукт = создатель -> создать ( ProductId :: MINE ); продукт -> печать (); продукт = создатель -> создать ( ProductId :: ВАШ ); продукт -> печать (); }
Вывод программы такой
this = 0x6e5e90 напечатайте МОЁ this = 0x6e62c0 напечатайте ВАШЕ
В игру-лабиринт можно играть в двух режимах: один с обычными комнатами, которые соединены только с соседними комнатами, и другой с волшебными комнатами, которые позволяют игрокам перемещаться в случайном порядке.
Room
является базовым классом для конечного продукта ( MagicRoom
или OrdinaryRoom
). MazeGame
объявляет абстрактный фабричный метод для производства такого базового продукта. MagicRoom
и OrdinaryRoom
являются подклассами базового продукта, реализующими конечный продукт. MagicMazeGame
и OrdinaryMazeGame
являются подклассами MazeGame
реализации фабричного метода производства конечной продукции. Таким образом, фабричные методы отделяют вызывающие объекты ( MazeGame
) от реализации конкретных классов. Это делает «нового» Оператора ненужным, позволяет придерживаться принципа открытости/закрытости и делает конечный продукт более гибким в случае изменений.
// Пустой словарь фактического объекта public интерфейс IPerson { string GetName (); } public class Villager : IPerson { public string GetName () { return "Village Person" ; } } общественный класс CityPerson : IPerson { public string GetName () { return "City Person" ; } } public enum PersonType { Сельский , Городской } /// <summary> /// Реализация Factory — используется для создания объектов. /// </summary> общественный класс PersonFactory { общественный IPerson GetPerson ( тип PersonType ) { переключатель ( тип ) { case PersonType . Сельский : вернуть нового жителя (); случай PersonType . Urban : вернуть нового CityPerson (); по умолчанию : создать новое исключение NotSupportedException (); } } }
В приведенном выше коде вы можете увидеть создание одного интерфейса под названием IPerson
и двух реализаций под названием Villager
и CityPerson
. В зависимости от типа, переданного в PersonFactory
объект, мы возвращаем исходный конкретный объект в качестве интерфейса IPerson
.
Фабричный метод — это просто дополнение к PersonFactory
классу. Он создает объект класса через интерфейсы, но, с другой стороны, он также позволяет подклассу решать, экземпляр какого класса создается.
общедоступный интерфейс IProduct { строка GetName (); bool SetPrice ( двойная цена ); } Телефон общественного класса : IProduct { частное двойное _price ; public string GetName () { return «Apple TouchPad» ; } public bool SetPrice ( двойная цена ) { _price = цена ; вернуть истину ; } } /* Почти то же самое, что и Factory, только дополнительная возможность сделать что-то с созданным методом */ public Abstract class ProductAbstractFactory { protected Abstract IProduct MakeProduct (); public IProduct GetObject () // Реализация фабричного метода. { вернуть это . СделатьПродукт (); } } общественный класс PhoneConcreteFactory : ProductAbstractFactory { защищенное переопределение IProduct MakeProduct () { продукт IProduct = новый телефон (); // Сделайте что-нибудь с объектом после того, как получите его. продукт . УстановитьЦена ( 20.30 ); возврат товара ; } }
Вы можете видеть, что мы использовали MakeProduct
в бетонной фабрике. В результате вы можете легко позвонить MakeProduct()
с него, чтобы получить файл IProduct
. Вы также можете написать свою собственную логику после получения объекта в конкретном фабричном методе. GetObject сделан абстрактным в интерфейсе Factory.
Этот пример Java похож на пример из книги « Шаблоны проектирования» .
MazeGame использует Rooms, но возлагает ответственность за создание Rooms на свои подклассы, которые создают конкретные классы. В обычном игровом режиме можно использовать этот шаблонный метод:
публичный абстрактный класс Room { Abstract void Connect ( Room room ); } общественный класс MagicRoom расширяет комнату { public void Connect ( комната комната ) { } } общественный класс OrdinaryRoom расширяет комнату { public void Connect ( комната комната ) { } } публичный абстрактный класс MazeGame { частный окончательный список < Комната > комнаты = новый ArrayList <> (); public MazeGame () { Комната room1 = makeRoom (); Комната комната2 = makeRoom (); комната1 . подключиться ( комната2 ); номера . добавить ( комната1 ); номера . добавить ( комната2 ); } абстрактная защищенная комната makeRoom (); }
В приведенном выше фрагменте MazeGame
конструктор представляет собой шаблонный метод , который реализует некоторую общую логику. Это относится к makeRoom
фабричному методу, который инкапсулирует создание комнат, позволяющих использовать другие комнаты в подклассе. Чтобы реализовать другой игровой режим с волшебными комнатами, достаточно переопределить метод makeRoom
:
общественный класс MagicMazeGame расширяет MazeGame { @Override protected MagicRoom makeRoom () { return new MagicRoom (); } } общественный класс OrdinaryMazeGame расширяет MazeGame { @Override protected OrdinaryRoom makeRoom () { return new OrdinaryRoom (); } } MazeGame обычная игра = новая обычная игра Maze (); MazeGame MagicGame = новая MagicMazeGame ();
Далее следует еще один пример на PHP , на этот раз с использованием реализации интерфейса вместо создания подклассов (однако того же можно добиться с помощью создания подклассов). Важно отметить, что фабричный метод также может быть определен как общедоступный и вызываться непосредственно клиентским кодом (в отличие от приведенного выше примера Java).
/* Заводские и автомобильные интерфейсы */интерфейс CarFactory { public function makeCar () : Car ; }интерфейс Car { общественная функция getType () : строка ; }/* Конкретные реализации фабрики и автомобиля */класс SedanFactory реализует CarFactory { public function makeCar () : Car { return new Sedan (); } }класс Sedan реализует Car { public function getType () : string { return 'Sedan' ; } }/* Клиент */$фабрика = новый SedanFactory (); $car = $factory -> makeCar (); напечатайте $car -> getType ();
То же, что и пример Java.
из abc импорт ABC , абстрактный методкласс MazeGame ( ABC ): def __init__ ( self ) -> None : self . комнаты = [] self . _prepare_rooms () def _prepare_rooms ( self ) -> Нет : room1 = self . make_room () комната2 = self . освободить место () комната1 . подключиться ( комната2 ) самостоятельно . номера . добавить ( room1 ) self . номера . добавить ( комната2 ) def play ( self ) -> None : print ( f "Игра с использованием { self.rooms [ 0 ] } " ) @abstractmethod def make_room ( self ): поднять NotImplementedError ( «Вы должны реализовать это!» )класс MagicMazeGame ( MazeGame ): def make_room ( self ) -> «MagicRoom» : return MagicRoom ()класс OrdinaryMazeGame ( MazeGame ): def make_room ( self ) -> «OrdinaryRoom» : return OrdinaryRoom ()class Room ( ABC ): def __init__ ( self ) -> None : self . подключенные_комнаты = [] def Connect ( self , room : «Комната» ) -> Нет : self . подключенные_комнаты . добавить ( комната )класс MagicRoom ( Комната ): def __str__ ( self ) -> str : вернуть «Волшебную комнату»класс OrdinaryRoom ( Комната ): def __str__ ( self ) -> str : return «Обычная комната»обычная игра = Обычная игра в лабиринте () обычная игра . играть ()MagicGame = MagicMazeGame () MagicGame . играть ()