Паттерн проектирования Builder в PHP
Шаблон проектирования PHP Builder — это пораждающий шаблон проектирования, который позволяет создавать определенные объекты классов с возможностью динамической установки свойств и объектов. Это полезно при создании объекта со многими возможными вариантами конфигурации.
Другими словами, в отличие от традиционных шаблонов создания, таких как Factory и Singletoon , которые имеют тенденцию создавать новый пустой экземпляр объекта, Builder создает полностью подготовленный объект, готовый для конкретной задачи. Примером этого является создание механизма установки для конкретного программного обеспечения. .
Итак, проблема в том, что нам нужен способ создания объекта с уже подготовленным набором функций. Давайте посмотрим пример: представьте, что у нас есть класс Vehicle, и нам нужно создать различные реализации этого класса, используя шаблон построителя.
Builder состоит из:
- Concrete class
- Интерфейс Builder
- Различные имплементации Builder
- Класс директора, который вызывает соответствующий конструктор и возвращает его.
Давайте рассмотрим каждый из этих элементов на примере транспортного средства.
Concrete class (Vehicle)
<?php // Concrete Class class Vehicle { public $model; public $enginesCount; public $type; const CAR = "Car"; const BUS = "Bus"; const TRAILER = "Trailer"; public function __construct() { } }
Интерфейс Builder
// Builder interface interface VehicleBuilderInterface { public function setModel(); public function setEnginesCount(); public function setType(); public function getVehicle(); }
Как показано в приведенном выше коде, интерфейс Builder содержит методы для подготовки построителя при его создании, а метод getVehicle вернет конечный объект.
Имплементация Builder
// Builder implementations class KiaCarBuilder implements VehicleBuilderInterface { private $vehicle; public function __construct(Vehicle $vehicle) { $this->vehicle = $vehicle; } public function setModel() { $this->vehicle->model = "Kia"; } public function setEnginesCount() { $this->vehicle->enginesCount = 1; } public function setType() { $this->vehicle->type = Vehicle::CAR; } public function getVehicle() { return $this->vehicle; } } class BmwBusBuilder implements VehicleBuilderInterface { private $vehicle; public function __construct(Vehicle $vechile) { $this->vehicle = $vechile; } public function setModel() { $this->vehicle->model = "Bmw"; } public function setEnginesCount() { $this->vehicle->enginesCount = 2; } public function setType() { $this->vehicle->type = Vehicle::BUS; } public function getVehicle() { return $this->vehicle; } } class ShevroletTrailerBuilder implements VehicleBuilderInterface { private $vehicle; public function __construct(Vehicle $vechile) { $this->vehicle = $vechile; } public function setModel() { $this->vehicle->model = "Shevrolet"; } public function setEnginesCount() { $this->vehicle->enginesCount = 2; } public function setType() { $this->vehicle->type = Vehicle::TRAILER; } public function getVehicle() { return $this->vehicle; } }
Как вы видите выше, мы только что создали несколько реализаций интерфейса Builder, передавая исходный конкретный класс в каждый конструктор, затем реализовали вспомогательные функции, такие как setModel и setType , которые, в свою очередь, предоставляют полный квалифицированный объект, и, наконец, мы вызвали getVehicle для возврата объекта Vehicle, выглядит это следующим образом:
public function getVehicle() { return $this->vehicle; }
Класс директора
Теперь последняя часть — это класс директора. Основная ответственность директора — получить интерфейс компоновщика и вызвать методы компоновщика, а затем получить объект.
class VehicleDirector { public function build(VehicleBuilderInterface $builder) { $builder->setModel(); $builder->setEnginesCount(); $builder->setType(); return $builder->getVehicle(); } }
Теперь, чтобы использовать Builder:
$kiaCar = (new VehicleDirector())->build(new KiaCarBuilder(new Vehicle())); $bmwBus = (new VehicleDirector())->build(new BmwBusBuilder(new Vehicle())); $shevroletTrailer = (new VehicleDirector())->build(new ShevroletTrailerBuilder(new Vehicle()));
Если вы выведете любой из этих экземпляров, например $kiaCar, вы получите экземпляр Vehicle с атрибутами, подготовленными для автомобиля Kia.
echo "<pre>"; var_dump($kiaCar); echo "\n"; echo "Vechile Model: " . $kiaCar->model . "\n"; echo "Vechile Engines: " . $kiaCar->enginesCount . "\n"; echo "Vechile Type: " . $kiaCar->type . "\n"; echo "<hr/>"; var_dump($bmwBus); echo "\n"; echo "Vechile Model: " . $bmwBus->model . "\n"; echo "Vechile Engines: " . $bmwBus->enginesCount . "\n"; echo "Vechile Type: " . $bmwBus->type . "\n"; echo "<hr/>"; var_dump($shevroletTrailer); echo "\n"; echo "Vechile Model: " . $shevroletTrailer->model . "\n"; echo "Vechile Engines: " . $shevroletTrailer->enginesCount . "\n"; echo "Vechile Type: " . $shevroletTrailer->type . "\n"; echo "<pre>";
Вывод
Кто-то может спросить, почему мы используем шаблон Builder если мы можем получить аналогичное поведение, если вы предоставите нашему конструктору необходимые параметры и получите тот же результат, и ответ таков: как мы упоминали в начале статьи, есть некоторые случаи когда нам нужно получить полный квалифицированный объект всего за одну строку кода, не выполняя все необходимые этапы инициализации, установки атрибутов и вызова методов.