Фабричный метод (шаблон проектирования)
Фабричный метод | |
---|---|
Factory Method | |
| |
Тип | Порождающий |
Назначение | Для создания объектов различных типов одним интерфейсом |
Плюсы | Создание объектов, независимо от их типов и сложности процесса создания. |
Минусы | Даже для одного объекта необходимо создать соответствующую фабрику, что увеличивает код. |
Описан в Design Patterns | Да |
Фабричный метод (англ. Factory Method), или виртуальный конструктор (англ. Virtual Constructor) — порождающий шаблон проектирования, предоставляющий подклассам (дочерним классам, субклассам) интерфейс для создания экземпляров некоторого класса. В момент создания наследники могут определить, какой класс создавать. Иными словами, данный шаблон делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не конкретные классы, а манипулировать абстрактными объектами на более высоком уровне.
Цель
[править | править код]Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, на основании какого класса создавать объект. Фабричный метод позволяет классу делегировать создание подклассов. Используется, когда:
- классу заранее неизвестно, объекты каких подклассов ему нужно создавать.
- класс спроектирован так, чтобы объекты, которые он создаёт, специфицировались подклассами.
- класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и планируется локализовать знание о том, какой класс принимает эти обязанности на себя.
Структура
[править | править код]- Product — продукт
- определяет интерфейс объектов, создаваемых абстрактным методом;
- ConcreteProduct — конкретный продукт
- реализует интерфейс Product;
- Creator — создатель
- объявляет фабричный метод, который возвращает объект типа Product. Может также содержать реализацию этого метода «по умолчанию»;
- может вызывать фабричный метод для создания объекта типа Product;
- ConcreteCreator — конкретный создатель
- переопределяет фабричный метод таким образом, чтобы он создавал и возвращал объект класса ConcreteProduct.
Достоинства
[править | править код]- позволяет сделать код создания объектов более универсальным, не привязываясь к конкретным классам (ConcreteProduct), а оперируя лишь общим интерфейсом (Product);
- позволяет установить связь между параллельными иерархиями классов.
Недостатки
[править | править код]- необходимость создавать наследника Creator для каждого нового типа продукта (ConcreteProduct).
Примеры кода
[править | править код]Kotlin
[править | править код]sealed interface Impact data class Culture(val description: String) : Impact data class Order(val description: String) : Impact sealed interface Government { fun produceImpact(): Impact } data object DemocracyGovernment : Government { override fun produceImpact(): Impact { return Culture("Original Movies") } } data object AuthoritarianGovernment : Government { override fun produceImpact(): Impact { return Order("Public Security") } } class State( private val name: String, private val government: Government ) { fun work() { println("State $name has made impact: ${government.produceImpact()}") } } fun main() { val dreamscapeState = State("Dreamscape Dominion", DemocracyGovernment) dreamscapeState.work() val nebulaState = State("Nebula Nations", AuthoritarianGovernment) nebulaState.work() }
Swift
[править | править код]protocol Product { func getName() -> String } class ConcreteProductA: Product { func getName() -> String { return "ConcreteProductA" } } class ConcreteProductB: Product { func getName() -> String { return "ConcreteProductB" } } protocol Creator { func factoryMethod() -> Product } class ConcreteCreatorA: Creator { func factoryMethod() -> Product { return ConcreteProductA() } } class ConcreteCreatorB: Creator { func factoryMethod() -> Product { return ConcreteProductB() } } let creatorA = ConcreteCreatorA() let creatorB = ConcreteCreatorB() let creators: [Creator] = [ creatorA, creatorB ] creators.forEach { let product = $0.factoryMethod() print(product.getName()) }
Python
[править | править код]# coding: utf-8 """Типы строя""" class Culture: """Культура""" def __repr__(self): return self.__str__() class Democracy(Culture): def __str__(self): return 'Democracy' class Dictatorship(Culture): def __str__(self): return 'Dictatorship' class Government: """Само правительство""" culture = '' def __str__(self): return self.culture.__str__() def __repr__(self): return self.culture.__repr__() def set_culture(self): """Задать строй правительству : это и есть наш Фабричный Метод""" raise AttributeError('Not Implemented Culture') class GovernmentA(Government): def set_culture(self): self.culture = Democracy() class GovernmentB(Government): def set_culture(self): self.culture = Dictatorship() g1 = GovernmentA() g1.set_culture() print(str(g1)) g2 = GovernmentB() g2.set_culture() print(str(g2))
Java
[править | править код]interface Product { } class ConcreteProductA implements Product { } class ConcreteProductB implements Product { } abstract class Creator { public abstract Product factoryMethod(); } class ConcreteCreatorA extends Creator { @Override public Product factoryMethod() { return new ConcreteProductA(); } } class ConcreteCreatorB extends Creator { @Override public Product factoryMethod() { return new ConcreteProductB(); } } public class FactoryMethodExample { public static void main(String[] args) { // an array of creators Creator[] creators = {new ConcreteCreatorA(), new ConcreteCreatorB()}; // iterate over creators and create products for (Creator creator: creators) { Product product = creator.factoryMethod(); System.out.printf("Created {%s}\n", product.getClass()); } } }
Результат работы:
- Created {class ConcreteProductA}
- Created {class ConcreteProductB}
C++
[править | править код]#include <iostream> #include <string> using namespace std; struct Product{ virtual string getName() = 0; virtual ~Product(){} }; struct ConcreteProductA: Product{ string getName(){return "ConcreteProductA";} }; struct ConcreteProductB: Product{ string getName(){return "ConcreteProductB";} }; struct Creator{ virtual Product* factoryMethod() = 0; }; struct ConcreteCreatorA: Creator{ Product* factoryMethod(){return new ConcreteProductA();} }; struct ConcreteCreatorB: Creator{ Product* factoryMethod(){return new ConcreteProductB();} }; int main() { ConcreteCreatorA CreatorA; ConcreteCreatorB CreatorB; // Массив создателей Creator*creators[] = {&CreatorA, &CreatorB}; //Перебирайте создателей и создавайте продукты for(auto&& creator: creators){ Product* product=creator->factoryMethod(); cout << product->getName() << endl; delete product; } return 0; }
Результат работы:
ConcreteProductA
ConcreteProductB
C#
[править | править код]using System; using System.Collections.Generic; namespace Factory { public abstract class Product { public abstract string GetType(); } public class ConcreteProductA : Product { public override string GetType() { return "ConcreteProductA"; } } public class ConcreteProductB : Product { public override string GetType() { return "ConcreteProductB"; } } public abstract class Creator { public abstract Product FactoryMethod(); } public class ConcreteCreatorA : Creator { public override Product FactoryMethod() { return new ConcreteProductA(); } } public class ConcreteCreatorB : Creator { public override Product FactoryMethod() { return new ConcreteProductB(); } } public static class MainApp { public static void Main() { // an array of creators Creator[] creators = { new ConcreteCreatorA(), new ConcreteCreatorB() }; // iterate over creators and create products foreach (Creator creator in creators) { Product product = creator.FactoryMethod(); Console.WriteLine("Created {0}", product.GetType()); } // Wait for user Console.Read(); } } }
JavaScript
[править | править код]var NewConcreteCreatorA=()=>{ return {factoryMethod:()=>{return {getName:()=>"ConcreteProductA"};}} }; var NewConcreteCreatorB=()=>{ return {factoryMethod:()=>{return {getName:()=>"ConcreteProductB"};}} }; var creators=[NewConcreteCreatorA(),NewConcreteCreatorB()]; creators.map(creator=>console.log(creator.factoryMethod().getName()));
class Product { GetName() {} } class ConcreteProductA extends Product { GetName() { return 'ProductA' } } class ConcreteProductB extends Product { GetName() { return 'ProductB' } } class Creator { FactoryMethod() {} } class ConcreteCreatorA extends Creator { FactoryMethod() { return new ConcreteProductA() } } class ConcreteCreatorB extends Creator { FactoryMethod() { return new ConcreteProductB() } } // An array of creators const creators = [ new ConcreteCreatorA(), new ConcreteCreatorB() ] const products = [] // Iterate over creators and create products for (let creator of creators) { products.push(creator.FactoryMethod().GetName()) } console.log(products)
interface Product { GetName(): string } class ConcreteProductA implements Product { public GetName() { return 'ProductA' } } class ConcreteProductB implements Product { public GetName() { return 'ProductB' } } interface Creator { FactoryMethod(): Product } class ConcreteCreatorA implements Creator { public FactoryMethod() { return new ConcreteProductA() } } class ConcreteCreatorB implements Creator { public FactoryMethod() { return new ConcreteProductB() } } // An array of creators const creators: Creator[] = [ new ConcreteCreatorA(), new ConcreteCreatorB() ] const products: string[] = [] // Iterate over creators and create products for (let creator of creators) { products.push(creator.FactoryMethod().GetName()) } console.log(products)
PHP5
[править | править код]<?php interface Product{ public function GetName(); } class ConcreteProductA implements Product{ public function GetName() { return "ProductA"; } } class ConcreteProductB implements Product{ public function GetName() { return "ProductB"; } } interface Creator{ public function FactoryMethod(); } class ConcreteCreatorA implements Creator{ public function FactoryMethod() { return new ConcreteProductA(); } } class ConcreteCreatorB implements Creator{ public function FactoryMethod() { return new ConcreteProductB(); } } // An array of creators $creators = array( new ConcreteCreatorA(), new ConcreteCreatorB() ); // Iterate over creators and create products foreach ($creators as $creator) { $products[] = $creator->FactoryMethod()->getName(); } header("content-type:text/plain"); echo var_export($products); ?>
PHP5 современный вариант
[править | править код]<?php /** * Class Animal * * Со времен первой редакции книги прошло более 20 лет и этот паттерн немного эволюционировал, * и теперь всегда используют его сокращенную форму */ abstract class Animal { // Фабричный метод, который на основе типа возвращает объект public static function initial($animal) { return new $animal(); } abstract public function voice(); } class Lion extends Animal { public function voice() { echo 'Rrrrrrrr i\'m the lion <br />' . PHP_EOL; } } class Cat extends Animal { public function voice() { echo 'Meow, meow i\'m the kitty <br />' . PHP_EOL; } } $lion = Animal::initial('Lion'); $cat = Animal::initial('Cat'); $lion->voice(); $cat->voice();
Delphi
[править | править код]program FactoryMethod; {$APPTYPE CONSOLE} uses SysUtils; type // Product TProduct = class(TObject) public function GetName: string; virtual; abstract; end; // ConcreteProductA TConcreteProductA = class(TProduct) public function GetName: string; override; end; // ConcreteProductB TConcreteProductB = class(TProduct) public function GetName: string; override; end; // Creator TCreator = class(TObject) public function FactoryMethod: TProduct; virtual; abstract; end; // ConcreteCreatorA TConcreteCreatorA = class(TCreator) public function FactoryMethod: TProduct; override; end; // ConcreteCreatorB TConcreteCreatorB = class(TCreator) public function FactoryMethod: TProduct; override; end; { ConcreteProductA } function TConcreteProductA.GetName: string; begin Result := 'ConcreteProductA'; end; { ConcreteProductB } function TConcreteProductB.GetName: string; begin Result := 'ConcreteProductB'; end; { ConcreteCreatorA } function TConcreteCreatorA.FactoryMethod: TProduct; begin Result := TConcreteProductA.Create; end; { ConcreteCreatorB } function TConcreteCreatorB.FactoryMethod: TProduct; begin Result := TConcreteProductB.Create; end; const Count = 2; var Creators: array[1..Count] of TCreator; Product: TProduct; I: Integer; begin // An array of creators Creators[1] := TConcreteCreatorA.Create; Creators[2] := TConcreteCreatorB.Create; // Iterate over creators and create products for I := 1 to Count do begin Product := Creators[I].FactoryMethod; WriteLn(Product.GetName); Product.Free; end; for I := 1 to Count do Creators[I].Free; ReadLn; end.
program FactoryMethod; {$APPTYPE CONSOLE} uses SysUtils; type // Product TProduct = class(TObject) private SubName : string; public function GetName: string; virtual; abstract; function GetFullName: string; constructor Create; virtual; abstract; end; TProductClass = class of TProduct; // ConcreteProductA TConcreteProductA = class(TProduct) public function GetName: string; override; constructor Create; override; end; // ConcreteProductB TConcreteProductB = class(TProduct) public function GetName: string; override; constructor Create; override; end; { TProduct} function TProduct.GetFullName: string; begin Result := GetName + ' : ' + SubName; end; { ConcreteProductA } constructor TConcreteProductA.Create; begin inherited; SubName := 'Product A subname'; end; function TConcreteProductA.GetName: string; begin Result := 'ConcreteProductA'; end; { ConcreteProductB } constructor TConcreteProductB.Create; begin inherited; SubName := 'Product B subname'; end; function TConcreteProductB.GetName: string; begin Result := 'ConcreteProductB'; end; const Count = 2; var Creators: array[1..Count] of TProductClass; Product: TProduct; I: Integer; begin // An array of creators Creators[1] := TConcreteProductA; Creators[2] := TConcreteProductB; // Iterate over creators and create products for I := 1 to Count do begin Product := Creators[I].Create; WriteLn(Product.GetFullName); Product.Free; end; ReadLn; end.
Action Script 3.0
[править | править код]protected class Creator { protected function factoryMethod():Product { return null; } public function someFunction():void { var _product:Product = factoryMethod(); _product.doSome(); } } public class ConcreteCreatorA extends Creator { override protected function factoryMethod():Product { return new ConcreteProductA(); } } public class ConcreteCreatorB extends Creator { override protected function factoryMethod():Product { return new ConcreteProductB(); } } public interface Product { function doSome():void{} } internal class ConcreteProductA implements Product { public function doSome():void{} } internal class ConcreteProductB implements Product { public function doSome():void{} } //РЕАЛИЗАЦИЯ public class Main { public function Main() { var _creatorA:ConcreteCreatorA = new ConcreteCreatorA(); _creatorA.someFunction(); var _creatorB:ConcreteCreatorB = new ConcreteCreatorB(); _creatorB.someFunction(); } }
Scala
[править | править код]abstract class AbstractProduct { def getName : String } abstract class AbstractCreator { def getProduct : AbstractProduct } class Beer extends AbstractProduct { override def getName: String = "Beer" } class Wine extends AbstractProduct { override def getName: String = "Wine" } class BeerCreator extends AbstractCreator { override def getProduct: AbstractProduct = new Beer } class WineCreator extends AbstractCreator { override def getProduct: AbstractProduct = new Wine } object Test { private def printProductName(creator: AbstractCreator) : Unit = println(creator.getProduct.getName) def main(args: Array[String]) : Unit = printProductName(new BeerCreator) printProductName(new WineCreator) }
Результат работы:
Created: Wine Created: Beer
Ruby
[править | править код]module FactoryMethod # Product class Product def class_name = self.class.name.split('::').last end # ConcreteProductA class ConcreteProductA < Product; end # ConcreteProductB class ConcreteProductB < Product; end # Creator class Creator def factoryMethod = Product.new end # ConcreteCreatorA class ConcreteCreatorA < Creator def factoryMethod = ConcreteProductA.new end # ConcreteCreatorB class ConcreteCreatorB < Creator def factoryMethod = ConcreteProductB.new end end # Client module Client include FactoryMethod creators = [ConcreteCreatorA.new, ConcreteCreatorB.new] creators.each do |creator| puts "#{creator.class} create Product:\t #{creator.factoryMethod.class_name}" end # => FactoryMethod::ConcreteCreatorA create Product: ConcreteProductA # => FactoryMethod::ConcreteCreatorB create Product: ConcreteProductB end
Ruby сокращенный вариант
[править | править код]class AbstractProduct def self.factory_method(product:) product.new end def product_name raise NotImplementedError, "#{self.class} не реализовал метод '#{__method__}'" end end class ProductA < AbstractProduct def product_name "Pizza" end end class ProductB < AbstractProduct def product_name "Milk" end end product_a = AbstractProduct.factory_method(product: ProductA) product_b = AbstractProduct.factory_method(product: ProductB) product_a.product_name product_b.product_name
Литература
[править | править код]- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно–ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб.: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1. (также ISBN 5-272-00355-1)