Structural Design Patterns provide different ways to create a class structure, for example using inheritance and composition to create a large object from small objects. Following design patterns come under this category.
Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class.
The Decorator pattern applies when there is a need to dynamically add as well as remove responsibilities to a class, and when subclassing would be impossible due to the large number of subclasses that could result. Decorators provide a alternative to subclassing for extending functionality.
The Decorator pattern is used to extend the functionality of an object dynamically without having to change the original class source or using inheritance. This is accomplished by creating an object wrapper referred to as a Decorator around the actual object.
Extending an object's functionality can be done statically (at compile time) by using inheritance however it might be necessary to extend an object's functionality dynamically (at runtime) as an object is used.
The Decorator object is designed to have the same interface as the underlying object. This allows a client object to interact with the Decorator object in exactly the same manner as it would with the underlying actual object. The Decorator object contains a reference to the actual object. The Decorator object receives all requests (calls) from a client. In turn, it forwards these calls to the underlying object. The Decorator object adds some additional functionality before or after forwarding requests to the underlying object. This ensures that the additional functionality can be added to a given object externally at runtime without modifying its structure.
The figure below shows a UML class diagram for the Decorator pattern:
The participants classes in the Decorator pattern are:
When to use Decorator pattern:
Related patterns:
Java
Suppose we want to implement different kinds of cars – we can create interface Car
to define the assemble
method and then we can have a Basic car
, further more we can extend it to Sports car
and Luxury car
.
But if we want to get a Car
at runtime that has both the features of Sports car
and Luxury car
, then the implementation gets complex and if further more we want to specify which features should be added first, it gets even more complex. Now imagine if we have ten different kind of cars, the implementation logic using inheritance and composition will be impossible to manage. To solve this kind of programming situation, we apply decorator pattern in Java.
We need to have following types to implement Decorator design pattern.
Component interface. The interface or abstract class defining the methods that will be implemented. In our case Car
will be the component interface.
public interface Car { public void assemble(); }
Concrete component. The basic implementation of the component interface. We can have BasicCar
class as our concrete component.
public class BasicCar implements Car { @Override public void assemble() { System.out.print("Basic Car."); } }
Decorator. Decorator
class implements the component interface and it has a HAS-A relationship with the component interface. The component variable should be accessible to the child decorator classes, so we will make this variable protected.
public class CarDecorator implements Car { protected Car car; public CarDecorator(Car c){ this.car = c; } @Override public void assemble() { this.car.assemble(); } }
Concrete decorators. Extending the base decorator functionality and modifying the component behavior accordingly. We can have concrete decorator classes as LuxuryCar
and SportsCar
.
public class SportsCar extends CarDecorator { public SportsCar(Car c) { super(c); } @Override public void assemble(){ super.assemble(); System.out.print("Adding features of Sports Car."); } }
public class LuxuryCar extends CarDecorator { public LuxuryCar(Car c) { super(c); } @Override public void assemble(){ super.assemble(); System.out.print("Adding features of Luxury Car."); } }
public class DecoratorPatternTest { public static void main(String[] args) { Car sportsCar = new SportsCar(new BasicCar()); sportsCar.assemble(); System.out.println("\n-----"); Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar())); sportsLuxuryCar.assemble(); } }
Python
import abc class Car(metaclass=abc.ABCMeta): @abc.abstractmethod def assemble(self): pass class BasicCar(Car): def assemble(self): print("Basic Car.") class CarDecorator(Car): def __init__(self, car): self.car = car def assemble(self): self.car.assemble() class SportsCar(CarDecorator): def __init__(self, car): super(SportsCar, self).__init__(car) def assemble(self): super(SportsCar, self).assemble() print("Adding features of Sports Car.") class LuxuryCar(CarDecorator): def __init__(self, car): super(LuxuryCar, self).__init__(car) def assemble(self): super(LuxuryCar, self).assemble() print("Adding features of Luxury Car.") if __name__ == '__main__': sports_car = SportsCar(BasicCar()) sports_car.assemble() print("-----") sports_luxury_car = SportsCar(LuxuryCar(BasicCar())) sports_luxury_car.assemble()