Фабричний метод (шаблон проєктування)
Фабричний метод (англ. Factory Method) — шаблон проєктування, відноситься до класу твірних шаблонів.
Призначення
Визначає інтерфейс для створення об'єкта, але залишає підкласам рішення про те, який саме клас інстанціювати. Фабричний метод дозволяє класу делегувати інстанціювання підкласам.
Застосування
Слід використовувати шаблон Фабричний метод коли:
- класу не відомо заздалегідь, об'єкти яких саме класів йому потрібно створювати;
- клас спроєктовано так, щоб об'єкти, котрі він створює, специфікувалися підкласами;
- клас делегує свої обов'язки одному з кількох допоміжних підкласів, та потрібно локалізувати знання про те, який саме підклас приймає ці обов'язки на себе.
Структура
- Product — продукт:
- визначає інтерфейс об'єктів, що створюються фабричним методом;
- ConcreteProduct — конкретний продукт:
- реалізує інтерфейс Product;
- Creator — творець:
- оголошує фабричний метод, що повертає об'єкт класу Product. Creator може також визначати реалізацію за умовчанням фабричного методу, що повертає об'єкт ConcreteProduct;
- може викликати фабричний метод для створення об'єкта Product;
- ConcreteCreator — конкретний творець:
- заміщує фабричний метод, що повертає об'єкт ConcreteProduct.
Переваги
- дозволяє зробити код створення об'єктів більш універсальним, не прив'язуючись до конкретних класів (ConcreteProduct), а оперуючи тільки загальним інтерфейсом (Продукт);
- дозволяє встановити зв'язок між паралельними ієрархіями класів.
Недоліки
- необхідність створювати спадкоємця Creator для кожного нового типу продукту (ConcreteProduct).
Стосунки
Творець покладається на свої підкласи в означенні фабричного методу, котрий буде повертати екземпляр відповідного конкретного продукту.
Реалізація
Деякі з сучасних мов програмування підтримують фабричний метод на рівні власних конструкцій таким чином, що ієрархія класів «Creator» не реалізовується. Дивись альтернативні реалізації нижче.
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
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}
C++
#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;
}
C#
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();
}
}
}
JavaScript
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());
});
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
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();
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.
Див. також
Література
Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5.