В объектно-ориентированном программировании шаблон метода фабрики — это шаблон проектирования , который использует методы фабрики для решения проблемы создания объектов без указания их точных классов . Вместо вызова конструктора это достигается путем вызова метода фабрики для создания объекта. Методы фабрики могут быть указаны в интерфейсе и реализованы подклассами или реализованы в базовом классе и опционально переопределены подклассами. Это один из 23 классических шаблонов проектирования, описанных в книге Шаблоны проектирования (часто называемой «Банда четырех» или просто «GoF»), и относится к подкатегории шаблона создания . [1]
Шаблон проектирования «Фабричный метод» решает такие проблемы, как:
Это позволяет создавать подклассы, которые могут изменить способ создания объекта (например, путем переопределения класса, экземпляр которого необходимо создать).
Согласно Design Patterns: Elements of Reusable Object-Oriented Software : «Определите интерфейс для создания объекта, но позвольте подклассам решать, какой класс создавать. Метод Factory позволяет классу откладывать создание используемого им экземпляра подклассам». [2]
Создание объекта часто требует сложных процессов, которые нецелесообразно включать в составной объект. Создание объекта может привести к значительному дублированию кода, может потребовать информации, недоступной для составного объекта, может не обеспечивать достаточный уровень абстракции или иным образом не быть включено в задачи составного объекта . Шаблон проектирования метода фабрики решает эти проблемы, определяя отдельный метод для создания объектов, который затем подклассы могут переопределять для указания производного типа продукта, который будет создан.
Шаблон метода фабрики опирается на наследование, поскольку создание объектов делегируется подклассам, которые реализуют метод фабрики для создания объектов. [3] Шаблон также может опираться на реализацию интерфейса .
В приведенной выше диаграмме классов UML класс , которому требуется объект, не создает экземпляр класса напрямую. Вместо этого ссылается на отдельный для создания объекта продукта, что делает независимым от конкретного класса, экземпляр которого создается. Подклассы могут переопределять, какой класс создавать. В этом примере подкласс реализует абстрактный класс , создавая экземпляр класса.Creator
Product
Product1
Creator
factoryMethod()
Creator
Creator
Creator1
factoryMethod()
Product1
Эта реализация C++14 основана на реализации до C++98, описанной в книге. [5] [ которая? ]
#include <iostream> #include <память> enum ProductId { МОЙ , ВАШ }; // определяет интерфейс объектов, создаваемых фабричным методом. class Product { public : virtual void print () = 0 ; virtual ~ Product () = default ; }; // реализует интерфейс Product. class ConcreteProductMINE : public Product { public : void print () { std :: cout << "this=" << this << " print MINE \n " ; } }; // реализует интерфейс Product. class ConcreteProductYOURS : public Product { public : void print () { std :: cout << "this=" << this << " print YOURS \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 < ConcreteProductYOURS > (); // повторите для оставшихся продуктов... return nullptr ; } virtual ~ Creator () = default ; }; int main () { // unique_ptr предотвращает утечки памяти. std :: unique_ptr < Creator > creator = std :: make_unique < Creator > (); std :: unique_ptr < Product > product = creator -> create ( ProductId :: MINE ); product -> print (); продукт = создатель -> создать ( ProductId :: YOURS ); продукт -> печать (); }
Вывод программы такой:
это = 0x6e5e90 распечатать МОЕ это = 0x6e62c0 распечатать ВАШЕ
В игру-лабиринт можно играть в двух режимах: в одном из обычных комнат, которые соединены только с соседними комнатами, и в другом с магическими комнатами, которые позволяют игрокам перемещаться случайным образом.
Room
является базовым классом для конечного продукта ( MagicRoom
или OrdinaryRoom
). MazeGame
объявляет абстрактный фабричный метод для создания такого базового продукта. MagicRoom
и OrdinaryRoom
являются подклассами базового продукта, реализующими конечный продукт. MagicMazeGame
и OrdinaryMazeGame
являются подклассами MazeGame
реализации фабричного метода, производящего конечные продукты. Таким образом, фабричные методы отделяют вызывающие объекты ( MazeGame
) от реализации конкретных классов. Это делает new
оператор избыточным, позволяет придерживаться принципа открытости-закрытости и делает конечный продукт более гибким в случае изменения.
// Пустой словарь реального объекта public interface IPerson { string GetName (); } public class Villager : IPerson { public string GetName () { return "Village Person" ; } } public class CityPerson : IPerson { public string GetName () { return "Городской человек" ; } } public enum PersonType { Сельский , Городской } /// <summary> /// Реализация Factory - используется для создания объектов. /// </summary> public class PersonFactory { public IPerson GetPerson ( PersonType type ) { switch ( type ) { case PersonType.Rural : return new Villager (); case PersonType.Urban : return new CityPerson ( ) ; default : throw new NotSupportedException ( ) ; } } }
Приведенный выше код иллюстрирует создание интерфейса с именем IPerson
и двух реализаций с именами Villager
и CityPerson
. Исходя из типа, переданного объекту PersonFactory
, исходный конкретный объект возвращается как интерфейс IPerson
.
Фабричный метод — это просто дополнение к PersonFactory
классу. Он создает объект класса через интерфейсы, но также позволяет подклассу решать, какой класс инстанцировать.
открытый интерфейс IProduct { string GetName (); bool SetPrice ( double price ); } публичный класс Телефон : IProduct { частный double _price ; public string GetName () { return "Apple TouchPad" ; } public bool SetPrice ( double price ) { _price = price ; return true ; } } /* Почти то же самое, что и Factory, просто дополнительная возможность что-то сделать с созданным методом */ public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct (); public IProduct GetObject () // Реализация метода Factory. { return this . MakeProduct (); } } public class PhoneConcreteFactory : ProductAbstractFactory { protected override IProduct MakeProduct () { IProduct product = new Phone (); // Сделать что -нибудь с объектом после его получения product.SetPrice ( 20.30 ) ; return product ; } }
В этом примере MakeProduct
используется в concreteFactory
. В результате MakeProduct()
может быть вызван для извлечения его из IProduct
. Пользовательская логика может выполняться после получения объекта в конкретном фабричном методе. GetObject
делается абстрактным в фабричном интерфейсе.
Этот пример Java похож на пример из книги « Шаблоны проектирования» .
Использует MazeGame
, Room
но делегирует ответственность за создание Room
объектов своим подклассам, которые создают конкретные классы. Обычный игровой режим может использовать этот шаблонный метод:
public abstract class Room { abstract void connect ( Room room ); } открытый класс MagicRoom расширяет Room { public void connect ( Room room ) {} } открытый класс OrdinaryRoom расширяет Room { public void connect ( Room room ) {} } public abstract class MazeGame { private final List < Room > rooms = new ArrayList <> (); public MazeGame ( ) { Комната room1 = makeRoom ( ); Комната room2 = makeRoom () ; room1.connect ( room2 ) ; rooms.add ( room1 ) ; rooms.add ( room2 ) ; } абстрактный защищенный Room makeRoom (); }
Конструктор MazeGame
— это шаблонный метод , который добавляет некоторую общую логику. Он ссылается на makeRoom()
метод фабрики, который инкапсулирует создание комнат таким образом, что другие комнаты могут использоваться в подклассе. Для реализации другого игрового режима, в котором есть магические комнаты, makeRoom
метод может быть переопределен:
открытый класс MagicMazeGame расширяет MazeGame { @Override protected MagicRoom makeRoom () { return new MagicRoom (); } } открытый класс OrdinaryMazeGame расширяет MazeGame { @Override protected OrdinaryRoom makeRoom () { return new OrdinaryRoom (); } } Игра-лабиринт обычная игра = новая обычная игра-лабиринт (); Игра-лабиринт магическая игра = новая магическая игра-лабиринт ();
Этот пример PHP показывает реализацию интерфейса вместо подклассификации (однако то же самое можно сделать и с помощью подклассификации). Метод фабрики также может быть определен как public
и вызван напрямую клиентским кодом (в отличие от предыдущего примера Java).
/* Интерфейсы завода и автомобиля */интерфейс CarFactory { public function makeCar () : Car ; }интерфейс Car { public function getType () : string ; }/* Конкретные реализации завода и автомобиля */класс SedanFactory реализует CarFactory { public function makeCar () : Car { return new Sedan (); } }класс Седан реализует Автомобиль { public function getType () : string { return 'Седан' ; } }/* Клиент */$factory = new SedanFactory (); $car = $factory -> makeCar (); print $car -> getType ();
В этом примере на Python используется то же самое, что и в предыдущем примере на Java.
из abc импорт ABC , абстрактный методкласс MazeGame ( ABC ): def __init__ ( self ) -> None : self . Rooms = [] self . _prepare_ Rooms () def _prepare_rooms ( self ) - > None : room1 = self.make_room ( ) room2 = self.make_room ( ) room1.connect ( room2 ) self.rooms.append ( room1 ) self.rooms.append ( room2 ) def play ( self ) -> None : print ( f "Игра с использованием { self . Rooms [ 0 ] } " ) @abstractmethod def make_room ( self ): raise NotImplementedError ( "Вы должны реализовать это!" )класс MagicMazeGame ( MazeGame ): def make_room ( self ) -> "MagicRoom" : return MagicRoom ()класс OrdinaryMazeGame ( MazeGame ): def make_room ( self ) -> "OrdinaryRoom" : return OrdinaryRoom ()класс Комната ( ABC ): def __init__ ( self ) -> None : self . connected_rooms = [] def connect ( self , room : "Комната" ) -> None : self . connected_rooms . append ( комната )класс MagicRoom ( Комната ): def __str__ ( self ) -> str : return "Волшебная комната"класс OrdinaryRoom ( Room ): def __str__ ( self ) -> str : return "Обычная комната"обычнаяИгра = ОбычнаяИграЛабиринт () обычнаяИгра . играть ()magicGame = MagicMazeGame () magicGame . play ()