Фабричний метод (англ. Factory Method) — шаблон проєктування, належить до класу твірних шаблонів.
Визначає інтерфейс для створення об'єкта, але залишає підкласам рішення про те, який саме клас інстанціювати. Фабричний метод дозволяє класу делегувати інстанціювання підкласам.
Слід використовувати шаблон Фабричний метод коли:
Творець покладається на свої підкласи в означенні фабричного методу, котрий буде повертати екземпляр відповідного конкретного продукту.
Деякі з сучасних мов програмування підтримують фабричний метод на рівні власних конструкцій таким чином, що ієрархія класів «Creator» не реалізовується. Дивись альтернативні реалізації нижче.
#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))
abstract class Product { } class ConcreteProductA extends Product { } class ConcreteProductB extends 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()); } } }
Результат роботи:
#include <iostream> #include <string> using namespace std; class Product { public: virtual string getName() = 0; virtual ~Product(){} }; class ConcreteProductA: public Product { public: string getName() { return "ConcreteProductA"; } }; class ConcreteProductB : public Product { public: string getName() { return "ConcreteProductB"; } }; class Creator { public: virtual Product* factoryMethod() = 0; }; class ConcreteCreatorA: public Creator { public: Product* factoryMethod() { return new ConcreteProductA(); } }; class ConcreteCreatorB : public Creator { public: Product* factoryMethod() { return new ConcreteProductB(); } }; int main() { static const size_t count = 2; ConcreteCreatorA CreatorA; ConcreteCreatorB CreatorB; // An array of creators Creator* creators[count] = {&CreatorA,&CreatorB}; // Iterate over creators and create products for (size_t i = 0; i < count; i++) { Product* product = creators[i]->factoryMethod(); cout << product->getName() << endl; delete product; } return 0; }
using System; using System.Collections.Generic; namespace Factory { abstract class Product { } class ConcreteProductA : Product { } class ConcreteProductB : Product { } abstract class Creator{ public abstract Product FactoryMethod(); } class ConcreteCreatorA : Creator{ public override Product FactoryMethod( ){ return new ConcreteProductA(); } } class ConcreteCreatorB : Creator{ public override Product FactoryMethod() { return new ConcreteProductB(); } } public 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(); } } }
using System; using System.Collections.Generic; using System.Collections.Concurrent; namespace FactoryMethod { // ускладнимо архітектуру // але забезпечимо повну незалежність класів у ієрархії // створює продукт public interface ICreator<out TReturnValue> { TReturnValue Create(); } // фабрика // реєструє, створює продукт public interface IFactory<in TKey, in TRegValue, out TReturnValue> where TRegValue : ICreator<TReturnValue> { void Registrate(TKey key, TRegValue value); void UnRegistrate(TKey key); TReturnValue MakeInstance(TKey key); } // ініціалізатор фабрики // наповнює фабрику початковими значеннями public interface IFactoryInitializer<in TFactory> { void Initialize(TFactory factory); } // узагальнений клас фабрики, public class FactoryBase<TKey, TReturnValue> : IFactory<TKey, ICreator<TReturnValue>, TReturnValue> { // FIELDS IDictionary<TKey, ICreator<TReturnValue>> factory; // CONSTRUCTORS public FactoryBase() { factory = new ConcurrentDictionary<TKey, ICreator<TReturnValue>>(); } // METHODS public TReturnValue MakeInstance(TKey key) { // checks if (key == null) throw new ArgumentNullException(nameof(key)); if (!factory.ContainsKey(key)) throw new InvalidOperationException($"No such key '{key}'"); // make instance return factory[key].Create(); } public void Registrate(TKey key, ICreator<TReturnValue> creator) { // checking argument // key if (key == null) throw new ArgumentNullException(nameof(key)); if (factory.ContainsKey(key)) throw new InvalidOperationException($"Type by key '{key}' has been already registered"); // value if (creator == null) throw new ArgumentNullException(nameof(creator)); Type creatorType = creator.GetType(); if (creatorType.IsInterface || creatorType.IsAbstract) throw new ArgumentException(nameof(creator)); // adding factory.Add(key, creator); } public void UnRegistrate(TKey key) { // checking argument // key if (key == null) throw new ArgumentNullException(nameof(key)); if (!factory.ContainsKey(key)) throw new InvalidOperationException($"No such key '{key}'"); // removing factory.Remove(key); } } // SHAPES // ієрархія класів abstract class ShapeBase { } class Square : ShapeBase { } class Circle : ShapeBase { } class Rectangle : ShapeBase { } // SHAPE CREATORS // для кожного класу в ієрархії варто написати // клас відповідальний за створення // (в C# не обов'язково, можна використати Activator) class SquareCreator : ICreator<Square> { public Square Create() { // можливо створення за допомогою // будь-якого, можливо твірного, шаблону проєктування return new Square(); } } class DefaultCreator<T> : ICreator<T> where T: new() { public T Create() { return new T(); } } class CircleCreator : DefaultCreator<Circle> { } // використовується при реєстрації // DefaultCreator<Rectangle> // зменшує кількість класів Creator'ів // та деколи краще мати окремі класи Creator'ів для кожного об'єкта // так при потребі змінити спосіб створення об'єкта, доведеться змінити лише відповідний метод Create // а не створювати новий клас, та шукати усі місця в яких він реєструється // FACTORY // конкретна фабрика class ShapeFactory : FactoryBase<string, ShapeBase> { public ShapeFactory(IFactoryInitializer<ShapeFactory> factoryInitializer) { factoryInitializer.Initialize(this); } } // конкретний ініціалізатор class DefaultShapeFactoryInitializer : IFactoryInitializer<ShapeFactory> { public void Initialize(ShapeFactory factory) { factory.Registrate(nameof(Square), new SquareCreator()); factory.Registrate(nameof(Circle), new CircleCreator()); factory.Registrate(nameof(Rectangle), new DefaultCreator<Rectangle>()); } } class Program { static void Main(string[] args) { ShapeFactory factory = new ShapeFactory(new DefaultShapeFactoryInitializer()); string[] shapeToCreate = { nameof(Square), nameof(Circle) }; foreach (string item in shapeToCreate) { ShapeBase shape = factory.MakeInstance(item); Console.WriteLine(shape.ToString()); } Console.ReadLine(); } } }
function Product() {this.getName=null;} 'use strict'; // Інстанціювання функції function ConcreteProductA() { this.getName = function() { // Повертання строки з вказаним змістом return 'ConcreteProductA'; }; } // Інстанціювання функції function ConcreteProductB() { this.getName = function() { // Повертання строки з вказаним змістом return 'ConcreteProductB'; }; } // Інстанціювання функції function ConcreteCreatorA() { this.factoryMethod = function() { // Повертання нової функції з рядка 7 return new ConcreteProductA(); }; } // Інстанціювання функції function ConcreteCreatorB() { this.factoryMethod = function() { // Повертання нової функції з рядка return new ConcreteProductB(); }; } // Створюємо масив функцій const creators = [new ConcreteCreatorA(), new ConcreteCreatorB()]; creators.forEach((el) => { console.log(el.factoryMethod().getName()); });
<?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 for($i = 0; $i < count($creators); $i++){ $products[] = $creators[$i]->FactoryMethod(); } header("content-type:text/plain"); echo var_export($products); ?>
<?php 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; } } $animal1 = Animal::initial('Lion'); $animal2 = Animal::initial('Cat'); $animal1->voice(); $animal2->voice();
<?php // Загальний Інтерфейс реалізації interface GuiFactoryInterface { public function buildButton(): ButtonInterface; public function buildCheckBox(): CheckBoxInterface; } interface ButtonInterface { public function draw(); } interface CheckBoxInterface { public function draw(); } // Кінцева реалізація class ButtonBootstrap implements ButtonInterface { public function draw() {return __CLASS__;} } class CheckBoxBootstrap implements CheckBoxInterface { public function draw() {return __CLASS__;} } class ButtonSemanticUI implements ButtonInterface { public function draw() {return __CLASS__;} } class CheckBoxSemanticUI implements CheckBoxInterface { public function draw() {return __CLASS__;} } // Інтерфейси для зв'язку однотипності // Групи взаємопов'язаних сімейств class BootstrapFactory implements GuiFactoryInterface { public function buildButton(): ButtonInterface { return new ButtonBootstrap(); } public function buildCheckBox(): CheckBoxInterface { return new CheckBoxBootstrap(); } } class SemanticUIFactory implements GuiFactoryInterface { public function buildButton(): ButtonInterface { return new ButtonSemanticUI(); } public function buildCheckBox(): CheckBoxInterface { return new CheckBoxSemanticUI(); } } // Опис роботи з об'єктом abstract class AbstractForm { public function render() { $guiKit = $this->createGuiKit(); $result[] = $guiKit->buildCheckBox()->draw(); $result[] = $guiKit->buildButton()->draw(); return $result; } abstract function createGuiKit(): GuiFactoryInterface; } class BootstrapDialogForm extends AbstractForm { public function createGuiKit(): GuiFactoryInterface { return new BootstrapFactory(); } } class SemanticUIDialogForm extends AbstractForm { public function createGuiKit(): GuiFactoryInterface { return new SemanticUIFactory(); } } function factoryMethod() { $dialogForm = new BootstrapDialogForm(); // OR //$dialogForm = new SemanticUIDialogForm(); return $dialogForm->render(); } $renderedDialogForm = factoryMethod();
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.
Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5.