Decorator pattern
Decorator pattern Overview
Its classified as structural design pattern as its used to form a large object structure for a specific object.
This pattern is mainly used to modify the behaviour of specific object at runtime.
It means we can modify the behaviour of specific object without altering the behaviour of other objects of the same class.
If we use inheritance or composition to extend the behaviour of object then its applicable to all the instances of the class and this happens at compile time itself, we can’t add behaviour at run time.
Decorator pattern is the perfect solution to add behaviour at runtime for a specific object without altering other objects of a class.
How it’s possible to modify the behaviour of one object of a class without altering other objects of same class? Decorator pattern uses abstract class or interface with composition to inject new behaviour at runtime
Let’s understand few terms before implementing this pattern
• Component
It’s an interface or abstract class defined for objects that can have additional responsibilities added to them.
• ConcreteComponent
It’s a java class which defines an object on which additional responsibilities will be added
• Decorator
It defines an interface that conforms to component’s interface. In other words, it extends Component
• ConcreteDecorator
It’s a java class which adds responsibilities to the component object.In other words, It just decorates the concrete component
Requirement:
Provide additional functionality called “Color” to few electronics Products in our system at runtime.
Let’s implement this pattern for above requirement
Product(Component)
o It is an interface which creates a blue print for the class which uses decorators
ElectronicProduct(ConcreteComponent)
o Object of ElectronicProduct class will be decorated by providing additional responsibilities to it dynamically.
• ProductDecorator(Decorator)
o It acts as a base decorator
• ProductColorDecorator(ConcreteDecorator)
o ProductColorDecorator will add additional responsibility i.e., adds color to Product.
Let’s convert above diagram into code to implement this pattern
Step 1
Create an Interface called Product and add a method called displayProduct()
- public interface Product{
- public String displayProduct();
- }
public interface Product{ public String displayProduct(); }
Step 2
Create a concrete class called “ElectronicsProduct” by implementing above interface
- public class ElectronicsProduct implements Product {
- @Override
- public String displayProduct() {
- return "Electronic Product";
- }
- }
public class ElectronicsProduct implements Product { @Override public String displayProduct() { return "Electronic Product"; } }
Step 3
Create an abstract decorator called “ProductDecorator”
- abstract class ProductDecorator implements Product{
- public abstract String displayProduct();
- }
abstract class ProductDecorator implements Product{ public abstract String displayProduct(); }
Step 4
Create a concrete decorator class called “ProductColorDecorator” by implementing above abstract decorator
- public class ProductColorDecorator extends ProductDecorator {
- protected Product product;
- public ProductColorDecorator (Product product) {
- this.product=product;
- }
- @Override
- public String displayProduct() {
- return product.displayProduct() +" -> Red Color";
- }
- }
public class ProductColorDecorator extends ProductDecorator { protected Product product; public ProductColorDecorator (Product product) { this.product=product; } @Override public String displayProduct() { return product.displayProduct() +" -> Red Color"; } }
Step 5
Test the pattern
- public class DecoratorDesignPatternTest {
- public static void main(String args[]) {
- Product product1 =new ElectronicsProduct(); // No decorator
- System.out.println(product1.displayProduct());
- Product product2 = new ProductColorDecorator(new ElectronicsProduct())); //Decorator for object of same class
- System.out.println(product2.displayProduct());
- }
- }
public class DecoratorDesignPatternTest { public static void main(String args[]) { Product product1 =new ElectronicsProduct(); // No decorator System.out.println(product1.displayProduct()); Product product2 = new ProductColorDecorator(new ElectronicsProduct())); //Decorator for object of same class System.out.println(product2.displayProduct()); } }
When to use this pattern?
When we have a requirement of modifying the object behaviour at runtime
Whenever we want to provide additional responsibilities or behaviour to few objects of a class rather than for all the objects of the same class.
In other words, we can add functionality to a single object and leave others unmodified
When we need to avoid too much of sub-classing to add additional responsibilities
When we need to achieve open closed principle(Open for extension and Closed for modification)
Note:
Java I/O Streams implementation has already implemented this pattern.