Сайт вебмастера

Шаблон Abstract Factory (Абстрактная фабрика) на PHP

02-07-2019Время чтения ~ 2 мин.PHP/ООП 10860

Паттерн «Абстрактная фабрика» довольно распространён, особенно часто приходится слышать выражение «использовать фабрику» среди программистов, которые делают вид, что разбираются в шаблонах проектирования. :)

Строго говоря, «фабрик» две: абстрактная фабрика и фабричный метод. Основная суть у них похожа, но разнится реализация. Поэтому желательно уточнять какая именно фабрика используется. Сейчас поговорим про Abstract Factory.

Исходный демо-пример вы найдета на гитхабе, посмотрите его, прежде читать дальше.

Главный смысл абстрактной фабрики в том, чтобы иметь несколько однотипных объектов с гарантированным методом (интерфейсом), но при этом вынести саму реализацию в отдельный класс. Таким образом каждая фабрика — это какой-то класс, который инстанцирует свою «рабочую лошадку».

В демо-примере используется интерфейс FactoryInterface, который содержит метод, который должен быть реализован в классах фабрик: ConcreteFactoryA и ConcreteFactoryB. То есть это гарантия, что эти классы будут обязательно содержать нужные методы. В нашем случае это method().

Класс ConcreteFactoryA для этого метода будет использовать класс Class1, который может быть уже произвольным. Главное, чтобы родительский класс о нём знал. Поэтому, когда потребуется внести изменения в работу фабрики, то достаточно будет редактировать только Class1. При этом, если у фабрик будет несколько методов (мы их формируем через интерфейс), то можно каждую (конечную) реализацию вынести в другие классы (Class3, Class4 и т.п.).

При желании можно всю реализацию оставить и в самом классе фабрики без использования дополнительных классов.

При этом, заметьте, мы создаём только класс фабрики, а остальные классы спрятаны.

/**
 * create FactoryA
 */
$a = new ConcreteFactoryA();
$a->method(); // Class1 method
 
/**
 * create FactoryB
 */
$b = new ConcreteFactoryB();
$b->method(); // Class2 method

Зачем нужны фабрики?

В первую очередь для унификации классов, поскольку они наследуются от единого интерфейса. Также мы точно знаем какие методы будут в фабрике, а значит фабрику можно использовать ту, что подходит под конкретную задачу. Вот примерно так:

$type_file = 'zip'; // rar, arj
 
if ($type_file == 'zip') {
	$f = new ZipFactory();
} elseif ($type_file == 'rar') {
	$f = new RarFactory();
} else {
	$f = new OtherFactory();
}
 
$f->pack();

То есть внешне мы работаем только с $f->pack(), хотя конкретная реализация будет зависеть от используемой фабрики.

Похожие записи
Комментарии (8) RSS
1 Andrii 2019-07-03 08:27:32

Хоть убейте не понимаю, зачем нужны дополнительные классы Class1 и Class2!

Почему реализацию метода method() нельзя организовать в ConcreteFactoryA и ConcreteFactoryB? Зачем вообще в этих классах нужно в конструкторах создавать экземпляры двух других классов, чтобы вызывать методы тех классов!?

В чём прикол?

Тут или пример сильно утрированный или одно из двух!


2 Admin 2019-07-03 09:17:03 admin

Пример, конечно утрирован, но сильно приближен к практике. Можно всё реализовать и в ConcreteFactoryA и ConcreteFactoryB, но тогда это будут обычные классы, связанные общим интерфейсом. В реальном проекте используется композиция, которая и делается через Class1 и Class2. Чтобы было проще понять, представьте себе, что у вас совершено раздельные библиотеки (с разными методами) для работы с zip и rar файлами, на которые вы не можете влиять (например вы их используете через композер). Пряча их в фабрику, вы скрываете реализацию, а пользователю оставляете только внешние типовые методы для работы.


3 Andrii 2019-07-03 10:21:27

Ага, вот теперь понятнее - т.е. если есть сторонние классы с разными методами - вы их оборачиваете в свои классы с одинаковыми методами, чтобы не переписывать свой зависимы от них код. Ок, спасибо.

В примере бы тогда был бы смысл в Class1 и Class2 методы называть по разному, а в фабрике их вызывать - было бы нагляднее и понятнее. А то всё одинаковое и смысл немного теряется.


4 Admin 2019-07-03 10:26:34 admin

В данном случае слово «method» следует понимать как «любой метод». :)


5 Аноним 2020-01-04 19:59:07
Пряча их в фабрику, вы скрываете реализацию, а пользователю оставляете только внешние типовые методы для работы

А разве это не паттерн фасада?


6 Admin 2020-01-04 20:22:56 admin

Нет, паттерн это алгоритм. Многие паттерны скрывают реализацию одних классов от других. Разница только в том, как именно это реализуется.


7 Павел 2020-03-18 08:56:21

А если мы используем несколько похожих (однотипных?) фасадов, которые делают похожую задачу (но разная реализация внутри), и всех их объединяет какой-то интерфейс - то эти фасады превращаются в фабрики?


8 Admin 2020-03-18 09:18:06 admin

Если фасады однотипные, то наверное есть смысл переделать код на паттерн с использованием абстрактных классов. Это если хотя бы часть функционала повторяется. Или породить от одного интерфейса, чтобы обеспечить совместимость типа.

Но вообще фасад — это обычная композиция, никто не мешает её использовать в любом другом паттерне: фабрики, стратегия и т.п.