The Decorator Pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified.
How?
The pattern works by wrapping an existing object in a new object that adds additional functionality. The new object, called the decorator, implements the same interface as the original object, so it can be used in place of the original object without any changes to the code that uses it. The decorator object typically has a reference to the original object, which it uses to delegate calls to the original object's methods. The decorator can then add additional functionality to the original object's methods before or after calling the original method.
When?
It should be used when you need to add new functionality to an existing object without modifying the original object's code. This is often useful for adding optional functionality, such as logging or error handling. It's capable of creating different variants of an object, such as a red circle and a green circle. In this case, each decorator would implement the same interface as the original object, but would add different functionality to the original object.
Why?
The decorator pattern should not be used when you need to modify the original object's code. If you need to make changes to the original object's code, you should use inheritance instead of the decorator pattern. It should also not be used when you need to create a new object that is completely different from the original object. In this case, you should use composition instead of the decorator pattern. Nor should it be used when you need to modify the original object's code. If you need to make changes to the original object's code, you should use inheritance instead of the decorator pattern.
Sample Code
interface Shape {
public function draw();
}
class Circle implements Shape {
public function draw() {
echo "Drawing a circle.";
}
}
class RedShapeDecorator implements Shape {
private $shape;
public function __construct(Shape $shape) {
$this->shape = $shape;
}
public function draw() {
echo "Drawing a red ";
$this->shape->draw();
}
}
$circle = new Circle();
$redCircle = new RedShapeDecorator($circle);
$redCircle->draw();
In this example, we have a Circle
class that implements the Shape
interface. We also have a RedShapeDecorator
class that implements the Shape
interface. The RedShapeDecorator
class has a constructor that takes a Shape
object as a parameter. The RedShapeDecorator
class's draw()
method calls the draw()
method of the Shape
object that it was passed in, and then adds the text "Drawing a red " before the text that was drawn by the Shape
object.
When we run the code, it will print the following output:
Drawing a red circle.
Here's another interesting example:
interface Shape {
public function draw();
}
class Circle implements Shape {
public function draw() {
echo "Drawing a circle";
}
}
class Decorator implements Shape {
protected $shape;
public function __construct(Shape $shape) {
$this->shape = $shape;
}
public function draw() {
$this->shape->draw();
echo "Adding some decoration";
}
}
class RedShapeDecorator extends Decorator {
public function draw() {
parent::draw();
echo "Making the shape red";
}
}
$circle = new Circle();
$redCircle = new RedShapeDecorator($circle);
$redCircle->draw();
Which outputs
Drawing a circle
Adding some decoration
Making the shape red
As you can see, the RedShapeDecorator class decorates the Circle class by adding the ability to make the shape red. The RedShapeDecorator class inherits from the Decorator class, which allows it to be used interchangeably with any other Decorator object.
The Decorator Pattern is a powerful design pattern that can be used to add new functionality to existing objects without modifying the original objects' code. The decorator pattern is often useful for adhering to the Single Responsibility Principle and the Open-Closed Principle.