Абстрактная фабрика — это порождающий шаблон проектирования, который решает проблему создания целых семейств связанных продуктов, без указания конкретных классов продуктов. Шаблон полезен, когда есть несколько классов, зависящих друг от друга.
Абстрактную фабрику можно определить по методам, возвращающим фабрику, которая, в свою очередь, используется для создания конкретных продуктов, возвращая их через абстрактные типы или интерфейсы.
В разных языках программирования существуют различные подходы в реализации решений абстрактной фабрики, однако большинство решений базируется на взаимодействии следующих классов участников:
Абстрактная фабрика в JavaScript
Итак, у нас имеется несколько классов, реализующих метод с одним и тем же именем. Предположим, это классы, создающие двери: класс, реализующий деревянные двери, и класс реализующий железные двери.
class WoodenDoor { getDescription() { console.log('I am a wooden door') } } class IronDoor { getDescription() { console.log('I am an iron door') } }
Так же имеются узкие специалисты, создающие либо деревянные двери (класс Welder), либо железные (класс Carpenter).
class Welder { getDescription() { console.log('I can only fit iron doors') } } class Carpenter { getDescription() { console.log('I can only fit wooden doors') } }
Реализуем фабрики. В комплекте с каждой дверью идет нужный мастер:
// Деревянная фабрика возвращает плотника и деревянную дверь class WoodenDoorFactory { makeDoor(){ return new WoodenDoor() } makeFittingExpert() { return new Carpenter() } } // Железная фабрика возвращает сварщика и железную дверь class IronDoorFactory { makeDoor(){ return new IronDoor() } makeFittingExpert() { return new Welder() } }
Теперь, используя фабрики, создадим железную и деревянные двери:
woodenFactory = new WoodenDoorFactory() door = woodenFactory.makeDoor() expert = woodenFactory.makeFittingExpert() door.getDescription() // I am a wooden door expert.getDescription() // I can only fit wooden doors ironFactory = new IronDoorFactory() door = ironFactory.makeDoor() expert = ironFactory.makeFittingExpert() door.getDescription() // I am an iron door expert.getDescription() // I can only fit iron doors
Абстрактная фабрика в PHP и Laravel
Известно несколько реализаций абстрактной фабрики в 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(); } 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 ' . PHP_EOL; } } class Cat extends Animal { public function voice() { echo 'Meow, meow i\'m the kitty ' . PHP_EOL; } } $animal1 = Animal::initial('Lion'); $animal2 = Animal::initial('Cat'); $animal1->voice(); $animal2->voice();
Абстрактная фабрика также может быть реализована с помощью интерфейса. Пример:
interface PhoneFactory { public createPhone(); } class AndroidFactory implements PhoneFactory { public createPhone() { return new \AndroidPhone(); } } class IPhoneFactory implements PhoneFactory { public createPhone() { return new \IPhone(); } } if ($user->wantsIPhone()) { $factory = new IPhoneFactory; } else { $factory = new AndroidFactory; } $phone = factory->createPhone();