Абстрактна фабрика
Абстра́ктна фа́брика (англ. Abstract Factory) — шаблон проєктування, що забезпечує інкапсуляцію окремих фабрик під єдиною схемою, упускаючи їхню деталізацію. Належить до класу твірних шаблонів.
В типових випадках застосування, клієнтський код створює конкретну реалізацію абстрактної фабрики, а потім використовує загальний універсальний інтерфейс фабрики, для створення екземплярів об'єктів, які є частиною схеми. Клієнтський код не знає (або не бере до уваги), які саме конкретно об'єкти він отримує від цих фабрик, оскільки він використовує універсальний інтерфейс для їхнього створення. Шаблон розмежовує деталі реалізації множини об'єктів від їхнього загального використання в коді, оскільки створення об'єкта здійснюється за допомогою методів, що забезпечуються інтерфейсом фабрики.[1]
Призначення
Подає інтерфейс для утворення родин взаємозв'язаних або взаємозалежних об'єктів, не специфікуючи їхніх конкретних класів.
Застосування
Слід використовувати шаблон Абстрактна фабрика коли:
- система не повинна залежати від того, як утворюються, компонуються та представляються вхідні до неї об'єкти;
- вхідні до родини взаємозв'язані об'єкти повинні використовуватися разом і необхідно забезпечити виконання цього обмеження;
- система повинна конфігуруватися однією з родин складаючих її об'єктів;
- треба подати бібліотеку об'єктів, розкриваючи тільки їхні інтерфейси, але не реалізацію.
Структура
- AbstractFactory — абстрактна фабрика:
- оголошує інтерфейс для операцій, що створюють абстрактні об'єкти-продукти;
- ConcreteFactory — конкретна фабрика:
- реалізує операції, що створюють конкретні об'єкти-продукти;
- AbstractProduct — абстрактний продукт:
- оголошує інтерфейс для типу об'єкта-продукту;
- ConcreteProduct — конкретний продукт:
- визначає об'єкт-продукт, що створюється відповідною конкретною фабрикою;
- реалізує інтерфейс AbstractProduct;
- Client — клієнт:
- користується виключно інтерфейсами, котрі оголошенні у класах AbstractFactory та AbstractProduct.
Переваги
- ізолює конкретні класи;
- спрощує заміну сімейств продуктів;
- гарантує сполучуваність продуктів.
Недоліки
- Важко добавити підтримку нового виду продуктів
Відносини
- Зазвичай під час виконання створюється єдиний екземпляр класу ConcreteFactory. Ця конкретна фабрика створює об'єкти продукти, що мають досить визначену реалізацію. Для створення інших видів об'єктів клієнт повинен користуватися іншою конкретною фабрикою;
- AbstractFactory передоручає створення об'єктів продуктів своєму підкласу ConcreteFactory.
Реалізація
C++
#include <string>
#include <iostream>
using namespace std;
// Сімейство елементів
// Базовий клас для всіх іграшок
class Toy
{
protected:
string name;
public:
Toy(string name)
{
this->name = name;
}
};
// Конкретні реалізації
struct WoodenCat : public Toy
{
WoodenCat() : Toy("Wooden Cat") { cout << this->name << '\n'; }
};
struct TeddyCat : public Toy
{
TeddyCat() : Toy("Teddy Cat") { cout << this->name << '\n'; }
};
struct WoodenBear : public Toy
{
WoodenBear() : Toy("Wooden Bear") { cout << this->name << '\n'; }
};
struct TeddyBear : public Toy
{
TeddyBear() : Toy("Teddy Bear") { cout << this->name << '\n'; }
};
// Абстрактна Фабрика (Abstract Factory)
struct IToyFactory
{
virtual Toy * GetBear() = 0;
virtual Toy * GetCat() = 0;
};
// конкретна фабрика (concrete factory)
struct TeddyToysFactory : IToyFactory
{
virtual Toy * GetBear()
{
return new TeddyBear();
}
virtual Toy * GetCat()
{
return new TeddyCat();
}
};
// і ще одна конкретна фабрика
struct WoodenToysFactory : IToyFactory
{
virtual Toy * GetBear()
{
return new WoodenBear();
}
virtual Toy * GetCat()
{
return new WoodenCat();
}
};
// Client
void create(IToyFactory * toyFactory)
{
Toy * bear = toyFactory->GetBear();
Toy * cat = toyFactory->GetCat();
}
void main()
{
// Спочатку створимо «дерев'яну» фабрику
auto p1 = WoodenToysFactory();
auto p2 = TeddyToysFactory();
create(&p1);
// А тепер створимо «плюшеву» фабрику,
create(&p2);
}
Swift
Протокол
protocol Decimal {
func stringValue() -> String
// factory
static func make(string : String) -> Decimal
}
typealias NumberFactory = (String) -> Decimal
// Number implementations with factory methods
struct NextStepNumber : Decimal {
private var nextStepNumber : NSNumber
func stringValue() -> String { return nextStepNumber.stringValue }
// factory
static func make(string : String) -> Decimal {
return NextStepNumber(nextStepNumber:NSNumber(longLong:(string as NSString).longLongValue))
}
}
struct SwiftNumber : Decimal {
private var swiftInt : Int
func stringValue() -> String { return "\(swiftInt)" }
// factory
static func make(string : String) -> Decimal {
return SwiftNumber(swiftInt:(string as NSString).integerValue)
}
}
Абстрактна фабрика
enum NumberType {
case NextStep, Swift
}
class NumberHelper {
class func factoryFor(type : NumberType) -> NumberFactory {
switch type {
case .NextStep:
return NextStepNumber.make
case .Swift:
return SwiftNumber.make
}
}
}
Використання
let factoryOne = NumberHelper.factoryFor(.NextStep)
let numberOne = factoryOne("1")
numberOne.stringValue()
let factoryTwo = NumberHelper.factoryFor(.Swift)
let numberTwo = factoryTwo("2")
numberTwo.stringValue()
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();
}
}
class GuiKitFactory
{
public function getFactory($type): GuiFactoryInterface
{
switch ($type) {
case 'bootstrap':
$factory = new BootstrapFactory();
break;
case 'semantic_ui':
$factory = new SemanticUIFactory();
break;
default:
throw new Exception('Невідомий тип фабрики [' . $type . ']');
}
return $factory;
}
}
class CreationPatternController
{
private $guiKit;
public function __construct()
{
$this->guiKit = (new GuiKitFactory())->getFactory('bootstrap');
// OR
//$this->guiKit = (new GuiKitFactory())->getFactory('semantic_ui');
}
function AbstractFactory()
{
$result[] = $this->guiKit->buildCheckbox()->draw();
$result[] = $this->guiKit->buildButton()->draw();
return $result;
}
}
$drawer = new CreationPatternController();
$drawer->AbstractFactory();
Примітки
- Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). У Hendrickson, Mike; Loukides, Mike. Head First Design Patterns (paperback) 1. O'REILLY. с. 162. ISBN 978-0-596-00712-6. Процитовано 12 вересня 2012.
Джерела
Примітки
- Будай, Андрій (2012). Дизайн-патерни — просто, як двері (Українською). с. 90.
Література
Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5.