Паттерн проектирования Builder в PHP

Шаблон проектирования PHP Builder — это пораждающий шаблон проектирования, который позволяет создавать определенные объекты классов с возможностью динамической установки свойств и объектов. Это полезно при создании объекта со многими возможными вариантами конфигурации.

Другими словами, в отличие от традиционных шаблонов создания, таких как Factory и Singletoon , которые имеют тенденцию создавать новый пустой экземпляр объекта, Builder создает полностью подготовленный объект, готовый для конкретной задачи. Примером этого является создание механизма установки для конкретного программного обеспечения. .

Блок-схема шаблона 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 если мы можем получить аналогичное поведение, если вы предоставите нашему конструктору необходимые параметры и получите тот же результат, и ответ таков: как мы упоминали в начале статьи, есть некоторые случаи когда нам нужно получить полный квалифицированный объект всего за одну строку кода, не выполняя все необходимые этапы инициализации, установки атрибутов и вызова методов.