Design problem
Sometimes it is necessary to impose additional responsibilities on a separate object, not in the class as a whole. So, a library for building graphic in the user interface should "be able" to add a new property, say, frames or new behavior (for example, the ability to scroll to any element interface). Add new responsibilities are permissible through inheritance. However, this solution is static, and therefore not flexible enough.
Decorator
A more flexible approach is another: put the component in another object called the decorator. The Decorator is the structural pattern is used to add new responsibilities to an object dynamically without extending functionality. That lets you dynamically change the behavior of an object at runtime by wrapping them in an object of a decorator class.
Decorators are used to adding to all interface methods some behavior that is not part of the core functionality. Decorator pattern perfectly suited for the following tasks:- caching work results;
- measuring the execution time of methods;
- user access control.
- The Component is the interface for the objects that will be added new responsibilities by decorators;
- Concrete Component defines objects which implement the Component interface and will be added new responsibilities by concrete decorators;
- Decorator have reference to Component and overridden component method’s;
- Concrete Decorator extends Decorator class and adds new functions, state or properties without creating new classes;
Decorator pattern in JDK:
- Streams: java.io package;
- Collections: java.util package.
Practice example
Let’s look at the Decorator pattern at the example. Our components are software developers that make some job, that’s why we create Developer interface.
public interface Developer {
public String makeJob();
}
Next, create a concrete developer:
public class JavaDeveloper implements Developer {
public String makeJob() {
return "Write Java Code";
}
}
Now, describe developer decorator to add functionality to our developers dynamically:
public class DeveloperDecorator implements Developer {
private Developer developer;
public DeveloperDecorator(Developer developer) {
this.developer = developer;
}
public String makeJob() {
return developer.makeJob();
}
}
The concrete decorator is the senior java developer, which is a developer with additional responsibility - code reviewing:
public class SeniorJavaDeveloper extends DeveloperDecorator {
public SeniorJavaDeveloper(Developer developer) {
super(developer);
}
public String makeCodeReview() {
return "Make code review";
}
public String makeJob() {
return super.makeJob() + " " + makeCodeReview();
}
}
The second decorator is the team leader. He is also a developer, but he needs to communicate with customers and send week reports to them:
public class JavaTeamLead extends DeveloperDecorator {
public JavaTeamLead(Developer developer) {
super(developer);
}
public String sendWeekReport() {
return "Send week report to customers.";
}
public String makeJob() {
return super.makeJob() + " " + sendWeekReport();
}
}
Here is the demo of the Decorator pattern:
public class Task {
public static void main(string[] args) {
Developer developer = new JavaTeamLead(
new SeniorJavaDeveloper(
new JavaDeveloper()));
System.out.println(developer.makeJob());
}
}
Conclusion
Decorator pattern is applicable in the following cases:
- When you want to add new properties and functions to the object dynamically;
- When the extension of classes is superfluous.