On this page
9. Decorator
The Decorator pattern is a structural design pattern that allows you to dynamically add behavior to objects without affecting other objects of the same class. It provides a flexible alternative to subclassing for extending functionality. The pattern involves creating a set of decorator classes that are used to wrap concrete components.
Key Characteristics:
- Dynamic Behavior Addition: Allows for adding new behavior to objects at runtime.
- Open/Closed Principle: Extends functionality without modifying existing code.
- Transparency: Ensures that decorated objects can still be used in the same way as undecorated ones.
How It Works:
- Component Interface: Defines the interface for objects that can have responsibilities added dynamically.
- Concrete Component: Implements the component interface and defines the base behavior.
- Decorator: Implements the component interface and maintains a reference to a component object. It forwards requests to the wrapped component and can add additional behavior.
- Concrete Decorators: Extend the functionality of the component by adding specific behavior.
Example in PHP:
// Component Interface
interface Coffee {
public function cost();
}
// Concrete Component
class SimpleCoffee implements Coffee {
public function cost() {
return 5;
}
}
// Decorator Base Class
abstract class CoffeeDecorator implements Coffee {
protected $coffee;
public function __construct(Coffee $coffee) {
$this->coffee = $coffee;
}
public function cost() {
return $this->coffee->cost();
}
}
// Concrete Decorator A
class MilkDecorator extends CoffeeDecorator {
public function cost() {
return parent::cost() + 2;
}
}
// Concrete Decorator B
class SugarDecorator extends CoffeeDecorator {
public function cost() {
return parent::cost() + 1;
}
}
// Usage
$coffee = new SimpleCoffee();
echo "Cost of Simple Coffee: $" . $coffee->cost() . "\n"; // Output: Cost of Simple Coffee: $5
$milkCoffee = new MilkDecorator($coffee);
echo "Cost of Coffee with Milk: $" . $milkCoffee->cost() . "\n"; // Output: Cost of Coffee with Milk: $7
$sugarMilkCoffee = new SugarDecorator($milkCoffee);
echo "Cost of Coffee with Milk and Sugar: $" . $sugarMilkCoffee->cost() . "\n"; // Output: Cost of Coffee with Milk and Sugar: $8
Explanation:
- Component Interface:
Coffee
defines thecost()
method that all concrete components and decorators must implement. - Concrete Component:
SimpleCoffee
implements theCoffee
interface and provides a base implementation for thecost()
method. - Decorator Base Class:
CoffeeDecorator
implements theCoffee
interface and contains a reference to aCoffee
object. It delegates thecost()
call to the wrapped coffee object. - Concrete Decorators:
MilkDecorator
andSugarDecorator
extendCoffeeDecorator
to add their specific behavior (e.g., adding the cost of milk or sugar).
Benefits:
- Flexibility: Allows adding responsibilities to objects dynamically and independently.
- Scalability: Facilitates adding new functionalities without altering existing code.
- Reusability: Decorators can be combined and reused in various ways to achieve different functionalities.
Use Cases:
- UI Components: For adding features like borders, scrollbars, or other enhancements to UI elements.
- Logging and Monitoring: To add logging or monitoring functionality to existing objects without modifying their code.
The Decorator pattern provides a way to extend the functionality of objects in a flexible and reusable manner, allowing for dynamic changes to behavior without altering the original object.