Done!

The Decorator Pattern - Design Pattern in PHP


Decorator pattern allow us to dynamically extend the behaviour during a runtime, without having to resort to unnecessary inheritance.

With Decorator Pattern

interface CarService
{
  public function getCost();
  public function getDescription();
}

class BasicCarService implements CarService
{
  public function getCost()
  {
    return 10.00;
  }

  public function getDescription()
  {
    return 'Basic Car Service';
  }
}

class RepairWheelService implements CarService
{
  public function __construct(CarService $carService)
  {
    $this->carService = $carService;
  }

  public function getCost()
  {
    return 20 + $this->carService->getCost();
  }

  public function getDescription()
  {
    return $this->carService->getDescription() . ' and Repair Wheel';
  }
}

class EngineCheckupService implements CarService
{
  public function __construct(CarService $carService)
  {
    $this->carService = $carService;
  }

  public function getCost()
  {
    return 50 + $this->carService->getCost();
  }

  public function getDescription()
  {
    return $this->carService->getDescription() . ' and Engine Checkup';
  }
}

This makes extending a behaviour at runtime dynamic and flexible. For e.g., if we need just Basic Inspection:

echo (new BasicInspection())->getCost();

Basic Inspection and Engine Checkup:

$customerCost = (
    new EngineCheckupService(
        new BasicCarService
    )
);

echo $customerCost->getCost( );

Basic Inspection and Engine Checkup:

$customerCost = (
    new EngineCheckupService(
        new RepairWheelService(
            new BasicCarService
        )
    )
);

echo $customerCost->getCost( );

Without Decorator Pattern

class BasicInspection {
  public function getCost() {
    return 19;
  }
}

echo (new BasicInspection())->getCost();

If we need Basic Inspection and Oil Change we have to create another class

class BasicInspectionAndOilChange {
  public function getCost() {
    return 19 * 19;
  }
}

echo (new BasicInspectionAndOilChange())->getCost();

Likewise, if we need Basic Inspection, Oil Change and Tire Rotation - we need to create another class

class BasicInspectionAndOilChangeAndTireRotation {
  public function getCost() {
    return 19 * 19 + 10;
  }
}

echo (new BasicInspectionAndOilChangeAndTireRotation())
         ->getCost();

Very quicky it breaks down for number of reasons:

Firstly, the number of class required would be huge as many classes are needed for each combination.

Secondly, price are being hardcoded. That means if a Basic Inspection changes to 30, then this has to be updated in multiple places.