OOP Design Patterns: Decorator
As a Structural Pattern, Decorator is attaching functionality to an already existing object-class without modifying the object.
With this in mind, we can say it supports the Open/Close principle of Object-Oriented Principles S.O.L.I.D. Software entities should be open for extension but closed for modification.
A new class is created that inherits the functionality of a base Decorator class.It’s constructor is injected with another Decorator class, again extending the base Decorator.Thus, it is possible for the extented Decorator class to be added in any position in a chain of creating a new custom decorated object.
In simple words, functionality is added at runtime by changing the order of decorator classes.
Implementation
Using Java code we are going to create a Salad Interface and concrete classes implementing the Salad Interface. We will then create a decorator class SaladDecorator implementing the Salad Interface and having Salad object as a variable.
ChickenIngredientDecorator and TomatoIngredientDecorator are concrete classes implementing SaladDecorator.
DecoratorPattern will use ChickenIngredientDecorator and TomatoIngredientDecorator to decorate Salad objects.
public interface ISalad { public String addIngredient();}public class Salad implements ISalad { @Override
public String addIngredient() {
return "Added Ingredient";
}}public abstract class SaladDecorator implements ISalad { private ISalad salad; public SaladDecorator(ISalad salad) {
this.salad = salad;
} @Override
public String addIngredient() {
return salad.addIngredient();
}}public class TomatoIngredientDecorator extends SaladDecorator{
public TomatoIngredientDecorator(ISalad salad) {
super(salad);
}public String addIngredient() {
return super.addIngredient() + " " + prepareTomatoes();
}private String prepareTomatoes() {
System.out.println("preparing tomatoes...");
return "Tomatoes";
}}public class ChickenIngredientDecorator extends SaladDecorator {public ChickenIngredientDecorator(ISalad salad) {
super(salad);
}public String addIngredient() {
return super.addIngredient() + " " + prepareChickenPieces();
}private String prepareChickenPieces() {
System.out.println("Cutting chicken into small pieces...");
return "Chicken";
}}
Final step is to create 3 different Salad combinations
- Salad decorated with Chicken pieces
- Salad decorated with Chicken and Tomatoe pieces
- Salad decorated with double quantity Chicken and Tomatoe pieces
public static void main(String[] args) { ISalad salad = new ChickenIngredientDecorator(new Salad()); System.out.println("Chicken Salad"); System.out.println("-------------"); System.out.println(salad.addIngredient()); System.out.println(""); System.out.println(""); System.out.println(""); System.out.println("Chicken/Tomato Salad"); System.out.println("-------------"); ISalad salad2 = new ChickenIngredientDecorator(new TomatoIngredientDecorator(new Salad())); System.out.println(salad2.addIngredient()); System.out.println(""); System.out.println(""); System.out.println(""); System.out.println("Double Chicken/Tomato Salad"); System.out.println("-------------"); ISalad salad3 = new ChickenIngredientDecorator(new ChickenIngredientDecorator(new TomatoIngredientDecorator(new Salad()))); System.out.println(salad3.addIngredient());}
Run the Decorator ..
Chicken Salad-------------Cutting chicken into small pieces...Added Ingredient ChickenChicken/Tomato Salad-------------preparing tomatoes...Cutting chicken into small pieces...Added Ingredient Tomatoes ChickenDouble Chicken/Tomato Salad-------------preparing tomatoes...Cutting chicken into small pieces...Cutting chicken into small pieces...Added Ingredient Tomatoes Chicken Chicken
Concluding, as per above, 3 different Salads where created using multiple Decorator classes combinations. Thing to notice here, is there was no need to create multiple classes for each combination of ingredients, bur rather reusing the existing ones.
- Using this pattern provide flexibility instead of using static inheritance
- By extending the base Decorator class, new functionality is added with no need to change the existing-base class
- Great way to add/remove features to an object without affecting the existing base object.