Фабричний метод (шаблон проєктування)
Фабричний метод (англ. 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.