Фабричный метод (шаблон проектирования)
Фабричный метод | |
---|---|
Factory Method | |
| |
Тип | Порождающий |
Назначение | Для создания объектов различных типов одним интерфейсом |
Плюсы | Создание объектов, независимо от их типов и сложности процесса создания. |
Минусы | Даже для одного объекта необходимо создать соответствующую фабрику, что увеличивает код. |
Описан в Design Patterns | Да |
Фабричный метод (англ. Factory Method), или виртуальный конструктор (англ. Virtual Constructor) — порождающий шаблон проектирования, предоставляющий подклассам (дочерним классам, субклассам) интерфейс для создания экземпляров некоторого класса. В момент создания наследники могут определить, какой класс создавать. Иными словами, данный шаблон делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не конкретные классы, а манипулировать абстрактными объектами на более высоком уровне.
Цель
Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, на основании какого класса создавать объект. Фабричный метод позволяет классу делегировать создание подклассов. Используется, когда:
- классу заранее неизвестно, объекты каких подклассов ему нужно создавать.
- класс спроектирован так, чтобы объекты, которые он создаёт, специфицировались подклассами.
- класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и планируется локализовать знание о том, какой класс принимает эти обязанности на себя.
Структура
- Product — продукт
- определяет интерфейс объектов, создаваемых абстрактным методом;
- ConcreteProduct — конкретный продукт
- реализует интерфейс Product;
- Creator — создатель
- объявляет фабричный метод, который возвращает объект типа Product. Может также содержать реализацию этого метода «по умолчанию»;
- может вызывать фабричный метод для создания объекта типа Product;
- ConcreteCreator — конкретный создатель
- переопределяет фабричный метод таким образом, чтобы он создавал и возвращал объект класса ConcreteProduct.
Достоинства
- позволяет сделать код создания объектов более универсальным, не привязываясь к конкретным классам (ConcreteProduct), а оперируя лишь общим интерфейсом (Product);
- позволяет установить связь между параллельными иерархиями классов.
Недостатки
- необходимость создавать наследника Creator для каждого нового типа продукта (ConcreteProduct).
Примеры кода
Kotlin
sealed interface Impact
data class Culture(val description: String) : Impact
data class Order(val description: String) : Impact
sealed interface Government {
fun produceImpact(): Impact
}
data object DemocracyGovernment : Government {
override fun produceImpact(): Impact {
return Culture("Original Movies")
}
}
data object AuthoritarianGovernment : Government {
override fun produceImpact(): Impact {
return Order("Public Security")
}
}
class State(
private val name: String,
private val government: Government
) {
fun work() {
println("State $name has made impact: ${government.produceImpact()}")
}
}
fun main() {
val dreamscapeState = State("Dreamscape Dominion", DemocracyGovernment)
dreamscapeState.work()
val nebulaState = State("Nebula Nations", AuthoritarianGovernment)
nebulaState.work()
}
Swift
protocol Product {
func getName() -> String
}
class ConcreteProductA: Product {
func getName() -> String { return "ConcreteProductA" }
}
class ConcreteProductB: Product {
func getName() -> String { return "ConcreteProductB" }
}
protocol Creator {
func factoryMethod() -> Product
}
class ConcreteCreatorA: Creator {
func factoryMethod() -> Product { return ConcreteProductA() }
}
class ConcreteCreatorB: Creator {
func factoryMethod() -> Product { return ConcreteProductB() }
}
let creatorA = ConcreteCreatorA()
let creatorB = ConcreteCreatorB()
let creators: [Creator] = [ creatorA, creatorB ]
creators.forEach {
let product = $0.factoryMethod()
print(product.getName())
}
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
interface Product { }
class ConcreteProductA implements Product { }
class ConcreteProductB implements 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;
struct Product{
virtual string getName() = 0;
virtual ~Product(){}
};
struct ConcreteProductA: Product{
string getName(){return "ConcreteProductA";}
};
struct ConcreteProductB: Product{
string getName(){return "ConcreteProductB";}
};
struct Creator{
virtual Product* factoryMethod() = 0;
};
struct ConcreteCreatorA: Creator{
Product* factoryMethod(){return new ConcreteProductA();}
};
struct ConcreteCreatorB: Creator{
Product* factoryMethod(){return new ConcreteProductB();}
};
int main()
{
ConcreteCreatorA CreatorA;
ConcreteCreatorB CreatorB;
// Массив создателей
Creator*creators[] = {&CreatorA, &CreatorB};
//Перебирайте создателей и создавайте продукты
for(auto&& creator: creators){
Product* product=creator->factoryMethod();
cout << product->getName() << endl;
delete product;
}
return 0;
}
Результат работы:
ConcreteProductA
ConcreteProductB
C#
using System;
using System.Collections.Generic;
namespace Factory
{
public abstract class Product
{
public abstract string GetType();
}
public class ConcreteProductA : Product
{
public override string GetType() { return "ConcreteProductA"; }
}
public class ConcreteProductB : Product
{
public override string GetType() { return "ConcreteProductB"; }
}
public abstract class Creator
{
public abstract Product FactoryMethod();
}
public class ConcreteCreatorA : Creator
{
public override Product FactoryMethod() { return new ConcreteProductA(); }
}
public class ConcreteCreatorB : Creator
{
public override Product FactoryMethod() { return new ConcreteProductB(); }
}
public static 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();
}
}
}
JavaScript
var NewConcreteCreatorA=()=>{
return {factoryMethod:()=>{return {getName:()=>"ConcreteProductA"};}}
};
var NewConcreteCreatorB=()=>{
return {factoryMethod:()=>{return {getName:()=>"ConcreteProductB"};}}
};
var creators=[NewConcreteCreatorA(),NewConcreteCreatorB()];
creators.map(creator=>console.log(creator.factoryMethod().getName()));
class Product {
GetName() {}
}
class ConcreteProductA extends Product {
GetName() {
return 'ProductA'
}
}
class ConcreteProductB extends Product {
GetName() {
return 'ProductB'
}
}
class Creator {
FactoryMethod() {}
}
class ConcreteCreatorA extends Creator {
FactoryMethod() {
return new ConcreteProductA()
}
}
class ConcreteCreatorB extends Creator {
FactoryMethod() {
return new ConcreteProductB()
}
}
// An array of creators
const creators = [ new ConcreteCreatorA(), new ConcreteCreatorB() ]
const products = []
// Iterate over creators and create products
for (let creator of creators) {
products.push(creator.FactoryMethod().GetName())
}
console.log(products)
interface Product {
GetName(): string
}
class ConcreteProductA implements Product {
public GetName() {
return 'ProductA'
}
}
class ConcreteProductB implements Product {
public GetName() {
return 'ProductB'
}
}
interface Creator {
FactoryMethod(): Product
}
class ConcreteCreatorA implements Creator {
public FactoryMethod() {
return new ConcreteProductA()
}
}
class ConcreteCreatorB implements Creator {
public FactoryMethod() {
return new ConcreteProductB()
}
}
// An array of creators
const creators: Creator[] = [ new ConcreteCreatorA(), new ConcreteCreatorB() ]
const products: string[] = []
// Iterate over creators and create products
for (let creator of creators) {
products.push(creator.FactoryMethod().GetName())
}
console.log(products)
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
foreach ($creators as $creator) {
$products[] = $creator->FactoryMethod()->getName();
}
header("content-type:text/plain");
echo var_export($products);
?>
PHP5 современный вариант
<?php
/**
* Class Animal
*
* Со времен первой редакции книги прошло более 20 лет и этот паттерн немного эволюционировал,
* и теперь всегда используют его сокращенную форму
*/
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;
}
}
$lion = Animal::initial('Lion');
$cat = Animal::initial('Cat');
$lion->voice();
$cat->voice();
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.
Action Script 3.0
protected class Creator
{
protected function factoryMethod():Product { return null; }
public function someFunction():void
{
var _product:Product = factoryMethod();
_product.doSome();
}
}
public class ConcreteCreatorA extends Creator
{
override protected function factoryMethod():Product
{
return new ConcreteProductA();
}
}
public class ConcreteCreatorB extends Creator
{
override protected function factoryMethod():Product
{
return new ConcreteProductB();
}
}
public interface Product
{
function doSome():void{}
}
internal class ConcreteProductA implements Product
{
public function doSome():void{}
}
internal class ConcreteProductB implements Product
{
public function doSome():void{}
}
//РЕАЛИЗАЦИЯ
public class Main
{
public function Main()
{
var _creatorA:ConcreteCreatorA = new ConcreteCreatorA();
_creatorA.someFunction();
var _creatorB:ConcreteCreatorB = new ConcreteCreatorB();
_creatorB.someFunction();
}
}
Scala
abstract class AbstractProduct {
def getName : String
}
abstract class AbstractCreator {
def getProduct : AbstractProduct
}
class Beer extends AbstractProduct {
override def getName: String = "Beer"
}
class Wine extends AbstractProduct {
override def getName: String = "Wine"
}
class BeerCreator extends AbstractCreator {
override def getProduct: AbstractProduct = new Beer
}
class WineCreator extends AbstractCreator {
override def getProduct: AbstractProduct = new Wine
}
object Test {
private def printProductName(creator: AbstractCreator) : Unit =
println(creator.getProduct.getName)
def main(args: Array[String]) : Unit =
printProductName(new BeerCreator)
printProductName(new WineCreator)
}
Результат работы:
Created: Wine Created: Beer
Ruby
module FactoryMethod
# Product
class Product
def class_name = self.class.name.split('::').last
end
# ConcreteProductA
class ConcreteProductA < Product; end
# ConcreteProductB
class ConcreteProductB < Product; end
# Creator
class Creator
def factoryMethod = Product.new
end
# ConcreteCreatorA
class ConcreteCreatorA < Creator
def factoryMethod = ConcreteProductA.new
end
# ConcreteCreatorB
class ConcreteCreatorB < Creator
def factoryMethod = ConcreteProductB.new
end
end
# Client
module Client
include FactoryMethod
creators = [ConcreteCreatorA.new, ConcreteCreatorB.new]
creators.each do |creator|
puts "#{creator.class} create Product:\t #{creator.factoryMethod.class_name}"
end
# => FactoryMethod::ConcreteCreatorA create Product: ConcreteProductA
# => FactoryMethod::ConcreteCreatorB create Product: ConcreteProductB
end
Ruby сокращенный вариант
class AbstractProduct
def self.factory_method(product:)
product.new
end
def product_name
raise NotImplementedError, "#{self.class} не реализовал метод '#{__method__}'"
end
end
class ProductA < AbstractProduct
def product_name
"Pizza"
end
end
class ProductB < AbstractProduct
def product_name
"Milk"
end
end
product_a = AbstractProduct.factory_method(product: ProductA)
product_b = AbstractProduct.factory_method(product: ProductB)
product_a.product_name
product_b.product_name
Литература
- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно–ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб.: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1. (также ISBN 5-272-00355-1)