Шаблоны проектирования: Фабричный Метод
Russian (Pусский) translation by Sergey Zhuk (you can also view the original English article)
В предыдущей статье мы разобрали шаблон Простая Фабрика. Теперь в этой статье мы рассмотрим шаблон проектирования Фабричный Метод. В случае с Простой Фабрикой она предоставляет интерфейс для создания объектов, в то время как Фабричный метод делает тоже самое, но в добавок ко всему еще и позволяет подклассам выбирать какой класс инициализировать.
Для объяснения шаблона Простая Фабрика я использовал пример создания объекта машины с разными типами автомобилей, например Седан, Пикап и другие. Я продолжу использовать этот же пример для объяснения шаблона Фабричный Метод, так чтобы нам было легче продолжить.
Проблема
В простой фабрике мы создавали объект класса автомобиль, но в действительности же это было создание объекта автомобиля на основе типа автомобиля, который мы передавали. Это было хорошим началом для реализации задачи создания объектов автомобилей разных типов.
Раньше наша компания была ограничена продажей автомобилей только в Штатах, и имела лишь одно производство в Штатах. Но с течением времени компания расширялась и было принято решение начать продажи автомобилей в Великобритании. Так же необходимо наладить там производство автомобилей.
В обоих центрах производства наш старый код по созданию автомобилей исправно работал, но компания решает пойти на некоторые изменения в модели автомобиля, но только для машин в Великобритании. На этом этапе наш код для создания объектов автомобиля сломается, так как нам необходимо будет реализовать дополнительные возможности (атрибуты) для некоторых типов авто.
В этом случае у нас есть два варианта: так же модифицировать текущий код (несколько if else условий), чтобы получить нужный объект автомобиля или реструктурировать наши классы таким образом, чтобы они не нуждались в if else условиях если в будущем понадобится добавить еще несколько атрибутов для определенных типов авто.
Никто не хочет использовать первый подход, потому что обычно это похоже на заплатки в коде вместо реальной реализации. Далее вы увидите, как можно реализовать другой подход решения этой проблемы с помощью шаблона проектирования Фабричный Метод.
Решение
Данный шаблон проектирования попадает в категорию структурных шаблонов, так как он больше направлен на то, каким образом классы будут структурированы. Так давайте перейдем к решению проблемы, правильно структурируя наши классы.
В простой фабрике мы не использовали структуру, так как нам нужен был только один класс, ответственный за создание объектов автомобилей. Решая текущую проблему, нам необходимо создать отдельные классы фабрик для обоих центров производств, так чтобы мы были уверены, что все фабрики следуют одним и тем процессам при создании объектов. Таким образом, мы сделаем один интерфейс, который будут реализовывать обе фабрики в Штатах и Великобритании.
interface carFactory { public function buildCar($type); }
Теперь наш интерфейс для фабрик готов и пришло время создать фабрики автомобилей для Штатов и Великобритании.
// Factory class to build US based center class USCarFactory implements carFactory { public function __construct() { } public function buildCar($type){ $car = null; switch($type) { case 'suv': $car = new USSuvFactory(); break; case 'sedan': $car = new USSedanFactory(); break; default: $car = new USSedanFactory(); break; } return $car; } } // Factory class to build UK based center class UKCarFactory implements carFactory { public function __construct() { } public function buildCar($type){ $car = null; switch($type) { case 'suv': $car = new UKSuvFactory(); break; case 'sedan': $car = new UKSedanFactory(); break; default: $car = new UKSedanFactory(); break; } return $car; } }
На данном этапе наши классы фабрик готовы, и можно начать создавать наши классы автомобилей. Точно так же как фабрики, нам нужно, чтобы эти классы реализовывали некоторые обязательные методы, общие для всех классов автомобилей.
interface car { public function getLocation(); public function getType(); }
Мы добавили самые общие методы в наш класс для получения места положения и типа автомобиля. Затем мы реализуем наши конкретные классы так как показано ниже.
class USSuvFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'US'; $this->carType = 'SUV'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class USSedanFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'US'; $this->carType = 'Sedan'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class UKSuvFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'UK'; $this->carType = 'SUV'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class UKSedanFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'UK'; $this->carType = 'Sedan'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } }
Реализация конкретных классов готова, они по сути являются объектами класса автомобиль, основанными на местонахождении и типе автомобиля. Теперь все что осталось это использовать эту реализацию, чтобы увидеть как она решает проблему, описанную выше.
// US Car Factory $USFactory = new USCarFactory(); // US based SUV model $USSuv = $USFactory->buildCar('suv'); echo $USSuv->getLocation().' based '. $USSuv->getType(); // US based Sedan model $USSedan = $USFactory->buildCar('sedan'); echo $USSedan->getLocation().' based '. $USSedan->getType(); // UK Car Factory $UKFactory = new UKCarFactory(); // UK based SUV model $UKSuv = $UKFactory->buildCar('suv'); echo $UKSuv->getLocation().' based '. $UKSuv->getType(); // UK based Sedan model $UKSedan = $UKFactory->buildCar('sedan'); echo $UKSedan->getLocation().' based '. $UKSedan->getType();
Можно сказать, что мы эффективно создали объекты всех возможных типов автомобиля с местонахождением. Наши классы теперь хорошо структурированы, так что добавление новой фабрики не создаст особых проблем.
Добавление Нового Класса Фабрики.
Представим, что компания решила наладить новое производства в центральной Австралии.
Так как наши классы сейчас отлично структурированы, то добавление нового класса для создания объектов автомобиля для Австралийского центра производства не станет проблемой для нас.
// Factory class to build AUS based center class AUSCarFactory implements carFactory { public function __construct() { } public function buildCar($type){ $car = null; switch($type) { case 'suv': $car = new AUSSuvFactory(); break; case 'sedan': $car = new AUSSedanFactory(); break; default: $car = new AUSSedanFactory(); break; } return $car; } } // Concrete classes for AUS based center class AUSSuvFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'AUS'; $this->carType = 'SUV'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } class AUSSedanFactory implements car { private $location; private $carType; public function __construct() { $this->location = 'AUS'; $this->carType = 'Sedan'; } public function getLocation() { return $this->location; } public function getType() { return $this->carType; } } // AUS Car Factory $AUSFactory = new AUSCarFactory(); // AUS based SUV model $AUSSuv = $AUSFactory->buildCar('suv'); echo $AUSSuv->getLocation().' based '. $AUSSuv->getType(); // AUS based Sedan model $AUSSedan = $AUSFactory->buildCar('sedan'); echo $AUSSedan->getLocation().' based '. $AUSSedan->getType();
Подведем Итоги
Итак, мы увидели, как мы можем структурировать наши классы таким образом, чтобы их можно было расширять без внесения каких либо изменений в основные классы и клиентский код. Не стесняйтесь оставлять комментарии или писать в мне в твиттер @XpertDevelopers.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weekly