Абстрактная фабрика (англ. Abstract factory) — порождающий шаблон проектирования, предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов. Шаблон реализуется созданием абстрактного класса Factory, который представляет собой интерфейс для создания компонентов системы (например, для оконного интерфейса он может создавать окна и кнопки). Затем пишутся классы, реализующие этот интерфейс.
Плюсы
- изолирует конкретные классы;
- упрощает замену семейств продуктов;
- гарантирует сочетаемость продуктов.
Минусы
- сложно добавить поддержку нового вида продуктов.
Применение
- Система не должна зависеть от того, как создаются, компонуются и представляются входящие в неё объекты.
- Входящие в семейство взаимосвязанные объекты должны использоваться вместе и вам необходимо обеспечить выполнение этого ограничения.
- Система должна конфигурироваться одним из семейств составляющих её объектов.
- Требуется предоставить библиотеку объектов, раскрывая только их интерфейсы, но не реализацию.
Пример применения паттерна Абстрактная фабрика на PHP
Создадим фабрику по работе с графическим интерфейсом пользователя, которая будет возвращать инструментарий фреймворка.
class GuiKitFactory
{
/**
* @param string $type
* @return GuiFactoryInterface
* @throws Exception
*/
public function getFactory(string $type): GuiFactoryInterface
{
return match ($type) {
'bootstrap' => new BootstrapFactory(),
'semanticui' => new SemanticUIFactory(),
default => throw new Exception("Unknown factory type [{$type}]"),
};
}
}
Далее создаем интерфейс, который нужно реализовать для наших фабрик.
interface GuiFactoryInterface
{
/**
* @return ButtonInterface
*/
public function buildButton(): ButtonInterface;
/**
* @return CheckBoxInterface
*/
public function buildCheckBox(): CheckBoxInterface;
}
Добавляем фабрики
class BootstrapFactory implements GuiFactoryInterface
{
/**
* @return ButtonInterface
*/
public function buildButton(): ButtonInterface
{
return new ButtonBootstrap();
}
/**
* @return CheckBoxInterface
*/
public function buildCheckBox(): CheckBoxInterface
{
return new CheckBoxBootstrap();
}
}
class SemanticUIFactory implements GuiFactoryInterface
{
/**
* @return ButtonInterface
*/
public function buildButton(): ButtonInterface
{
return new ButtonSemanticUI();
}
/**
* @return CheckBoxInterface
*/
public function buildCheckBox(): CheckBoxInterface
{
return new CheckBoxSemanticUI();
}
}
Теперь создадим интерфейсы создаваемых элементов.
Button
interface ButtonInterface
{
public function draw();
}
Checkbox
interface CheckBoxInterface
{
public function draw();
}
Поскольку мы уже создали абстракцию нашей логики, теперь переходим к её реализации.
class ButtonBootstrap implements ButtonInterface
{
public function draw()
{
\Debugbar::info('Draw Button Bootstrap');
return "<button class='btn btn-success'>Button</button>";
}
}
class CheckBoxBootstrap implements CheckBoxInterface
{
public function draw()
{
\Debugbar::info('Draw CheckBox Bootstrap');
return "<div class='form-check'>
<input type='checkbox' class='form-check-input' id='exampleCheck1'>
<label class='form-check-label' for='exampleCheck1'>Check me out</label>
</div>";
}
}
class ButtonSemanticUI implements ButtonInterface
{
public function draw()
{
\Debugbar::info('Draw Button SemanticUI');
return "<button class='ui green button'>Button</button>";
}
}
class CheckBoxSemanticUI implements CheckBoxInterface
{
public function draw()
{
\Debugbar::info('Draw CheckBox SemanticUI');
return "<div class='ui checkbox checked'>
<input type='checkbox' tabindex='0' class='hidden'>
<label>I agree to the Terms and Conditions</label>
</div>";
}
}
Пример использования
Для начала попробуем использовать SemanticUI.
$guiKit = (new GuiKitFactory())->getFactory('semanticui');
$button = $guiKit->buildButton()->draw();
$checkbox = $guiKit->buildCheckBox()->draw();
А теперь мы захотели использовать Bootstrap, немного сменим логику...
$guiKit = (new GuiKitFactory())->getFactory('bootstrap');
$button = $guiKit->buildButton()->draw();
$checkbox = $guiKit->buildCheckBox()->draw();