Фабричний метод (шаблон проєктування)
Фабричний метод (англ. Factory Method) — шаблон проєктування, належить до клас�� твірних шаблонів.
Визначає інтерфейс для створення об'єкта, але залишає підкласам рішення про те, який саме клас інстанціювати. Фабричний метод дозволяє класу делегувати інстанціювання підкласам.
Слід використовувати шаблон Фабричний метод коли:
- класу не відомо заздалегідь, об'єкти яких саме класів йому потрібно створювати;
- клас спроєктовано так, щоб об'єкти, котрі він створює, специфікувалися підкласами;
- клас делегує свої обов'язки одному з кількох допоміжних підкласів, та потрібно локалізувати знання про те, який саме підклас приймає ці обов'язки на себе.
- Product — продукт:
- визначає інтерфейс об'єктів, що створюються фабричним методом;
- ConcreteProduct — конкретний продукт:
- реалізує інтерфейс Product;
- Creator — творець:
- оголошує фабричний метод, що повертає об'єкт класу Product. Creator може також визначати реалізацію за умовчанням фабричного методу, що повертає об'єкт ConcreteProduct;
- може викликати фабричний метод для створення об'єкта Product;
- ConcreteCreator — конкретний творець:
- заміщує фабричний метод, що повертає об'єкт ConcreteProduct.
- дозволяє зробити код створення об'єктів більш універсальним, не прив'язуючись до конкретних класів (ConcreteProduct), а оперуючи тільки загальним інтерфейсом (Продукт);
- дозволяє встановити зв'язок між паралельними ієрархіями класів.
- необхідність створювати спадкоємця Creator для кожного нового типу продукту (ConcreteProduct).
Творець покладається на свої підкласи в означенні фабричного методу, котрий буде повертати екземпляр відповідного конкретного продукту.
Деякі з сучасних мов програмування підтримують фабричний метод на рівні власних конструкцій таким чином, що ієрархія класів «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());
}
}
}
Результат роботи:
- Created {class ConcreteProductA}
- Created {class ConcreteProductB}
#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.