Фабричний метод (шаблон проєктування)

Фабричний метод (англ. Factory Method) — шаблон проєктування, належить до класу твірних шаблонів.

Призначення

Визначає інтерфейс для створення об'єкта, але залишає підкласам рішення про те, який саме клас інстанціювати. Фабричний метод дозволяє класу делегувати інстанціювання підкласам.

Застосування

Слід використовувати шаблон Фабричний метод коли:

  • класу не відомо заздалегідь, об'єкти яких саме класів йому потрібно створювати;
  • клас спроєктовано так, щоб об'єкти, котрі він створює, специфікувалися підкласами;
  • клас делегує свої обов'язки одному з кількох допоміжних підкласів, та потрібно локалізувати знання про те, який саме підклас приймає ці обов'язки на себе.

Структура

UML діаграма, що описує структуру шаблону проєктування Фабричний метод
  • Product — продукт:
    • визначає інтерфейс об'єктів, що створюються фабричним методом;
  • ConcreteProduct — конкретний продукт:
    • реалізує інтерфейс Product;
  • Creator — творець:
    • оголошує фабричний метод, що повертає об'єкт класу Product. Creator може також визначати реалізацію за умовчанням фабричного методу, що повертає об'єкт ConcreteProduct;
    • може викликати фабричний метод для створення об'єкта Product;
  • ConcreteCreator — конкретний творець:
    • заміщує фабричний метод, що повертає об'єкт ConcreteProduct.

Переваги

  • дозволяє зробити код створення об'єктів більш універсальним, не прив'язуючись до конкретних класів (ConcreteProduct), а оперуючи тільки загальним інтерфейсом (Продукт);
  • дозволяє встановити зв'язок між паралельними ієрархіями класів.

Недоліки

  • необхідність створювати спадкоємця Creator для кожного нового типу продукту (ConcreteProduct).

Стосунки

Творець покладається на свої підкласи в означенні фабричного методу, котрий буде повертати екземпляр відповідного конкретного продукту.

Реалізація

Деякі з сучасних мов програмування підтримують фабричний метод на рівні власних конструкцій таким чином, що ієрархія класів «Creator» не реалізовується. Дивись альтернативні реалізації нижче.

Python

Реалізація мовою 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

Реалізація мовою 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++

Реалізація мовою 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#

Реалізація мовою 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();
        }
    }
}
Реалізація мовою C#
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

Реалізація мовою 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
<?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
<?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
<?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

Реалізація мовою 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.
Реалізація мовою Delphi (віртуальні конструктори)
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.

Див. також

Джерела

  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns: Elements of Reusable Object-Oriented Software (вид. [1]). Addison–Wesley. с. 395. {{cite book}}: Зовнішнє посилання в |edition= (довідка)(англ.)
  • Alan Shallowey, James R. Trott (2004). Design Patterns Explained: A New Perspective on Object-Oriented Design (PDF).(англ.)
Prefix: a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9

Portal di Ensiklopedia Dunia

Kembali kehalaman sebelumnya