Sobes.tech
Middle

How does a decorator work in PHP?

sobes.tech AI

Answer from AI

The "Decorator" pattern dynamically adds new behavior to an existing object by wrapping it in another object called a decorator.

  • Components:

    • Component Interface: Declares common methods for the original object and its decorators.
    • Concrete Component: The main object that needs to be decorated.
    • Decorator Base Class: An abstract class or interface that contains a reference to a Component interface object and implements its methods, usually delegating calls to this object.
    • Concrete Decorators: Implement new capabilities, supplementing or modifying the behavior of the base component.
  • How it works:

    • The decorator contains a reference to the object it decorates (composition).
    • The decorator has the same interface as the decorated object.
    • When a method is called on the decorator, it either performs additional logic before or after calling the corresponding method on the decorated object, or completely replaces it (though the latter is not typical usage).
  • Advantages:

    • Allows adding functionality to objects at runtime, rather than in static inheritance.
    • Avoids "class hierarchy explosion" when combining multiple behaviors.
    • Enables creating flexible behavior combinations.
  • Example:

// Component Interface
interface Coffee {
    public function getCost(): float;
    public function getDescription(): string;
}

// Concrete Component
class SimpleCoffee implements Coffee {
    public function getCost(): float {
        return 5.0;
    }

    public function getDescription(): string {
        return "Regular coffee";
    }
}

// Base Decorator
abstract class CoffeeDecorator implements Coffee {
    protected Coffee $coffee;

    public function __construct(Coffee $coffee) {
        $this->coffee = $coffee;
    }

    public function getCost(): float {
        return $this->coffee->getCost();
    }

    public function getDescription(): string {
        return $this->coffee->getDescription();
    }
}

// Concrete Decorator: Adding milk
class MilkDecorator extends CoffeeDecorator {
    public function getCost(): float {
        return parent::getCost() + 2.0;
    }

    public function getDescription(): string {
        return parent::getDescription() . ", with milk";
    }
}

// Concrete Decorator: Adding sugar
class SugarDecorator extends CoffeeDecorator {
    public function getCost(): float {
        return parent::getCost() + 1.0;
    }

    public function getDescription(): string {
        return parent::getDescription() . ", with sugar";
    }
}

// Usage:
// Create a regular coffee
$coffee = new SimpleCoffee();
echo $coffee->getDescription() . " - " . $coffee->getCost() . "\n"; // Regular coffee - 5

// Add milk
$coffeeWithMilk = new MilkDecorator($coffee);
echo $coffeeWithMilk->getDescription() . " - " . $coffeeWithMilk->getCost() . "\n"; // Regular coffee, with milk - 7

// Add sugar as well
$coffeeWithMilkAndSugar = new SugarDecorator($coffeeWithMilk);
echo $coffeeWithMilkAndSugar->getDescription() . " - " . $coffeeWithMilkAndSugar->getCost() . "\n"; // Regular coffee, with milk, with sugar - 8