The Decorator design pattern is a structural pattern that allows behavior to be added to objects, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is typically used to extend the functionalities of classes in a flexible and reusable way.
Key Concepts
- Component Interface: Defines the interface for objects that can have responsibilities added to them dynamically.
- Concrete Component: The class to which additional responsibilities can be attached.
- Decorator: Maintains a reference to a component object and defines an interface that conforms to the component's interface.
- Concrete Decorators: Extend the functionality of the component by adding new behavior.
Example in PHP:
Let's consider an example where we have a Coffee interface and several implementations and decorators.
Step 1: Define the Component Interface
interface Coffee { public function getCost(): int; public function getDescription(): string; }
Step 2: Create Concrete Component
class SimpleCoffee implements Coffee { public function getCost(): int { return 5; } public function getDescription(): string { return "Simple Coffee"; } }
Step 3: Create Decorator Base Class
class CoffeeDecorator implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost(): int { return $this->coffee->getCost(); } public function getDescription(): string { return $this->coffee->getDescription(); } }
Step 4: Create Concrete Decorators
class MilkDecorator extends CoffeeDecorator { public function getCost(): int { return $this->coffee->getCost() + 2; } public function getDescription(): string { return $this->coffee->getDescription() . ", Milk"; } } class SugarDecorator extends CoffeeDecorator { public function getCost(): int { return $this->coffee->getCost() + 1; } public function getDescription(): string { return $this->coffee->getDescription() . ", Sugar"; } }
Step 5: Usage
// Simple coffee $someCoffee = new SimpleCoffee(); echo $someCoffee->getCost(); // 5 echo $someCoffee->getDescription(); // Simple Coffee // Add milk $someCoffee = new MilkDecorator($someCoffee); echo $someCoffee->getCost(); // 7 echo $someCoffee->getDescription(); // Simple Coffee, Milk // Add sugar $someCoffee = new SugarDecorator($someCoffee); echo $someCoffee->getCost(); // 8 echo $someCoffee->getDescription(); // Simple Coffee, Milk, Sugar
Explanation
- Component Interface: Coffee defines methods getCost and getDescription.
- Concrete Component: SimpleCoffee implements the Coffee interface.
- Decorator Base Class: CoffeeDecorator implements Coffee and holds an instance of Coffee.
- Concrete Decorators: MilkDecorator and SugarDecorator extend CoffeeDecorator and add new functionality.
Benefits
- Single Responsibility Principle: You can divide a complex task into simple, single-purpose classes.
- Open/Closed Principle: You can extend the behavior of a class without modifying existing code.
- Flexible Decoration: You can combine multiple decorators to create different combinations of behavior.
By using the Decorator pattern, you can dynamically enhance the functionality of objects in a flexible and reusable way without modifying their structure.