The article you’re about to read is part of a bigger series about software design patterns implemented on languages using the JVM, mostly Java. In this one the Decorator pattern is presented.
Table of contents
Description
Gang of Four definition: Dynamically attach additional responsabilities to an object. Decorators provide a flexible alternative to subclassing for extending functionality.
This pattern is related with the Open-Closed principle from SOLID, that means we have a class closed for modification but open for extension. The decorator pattern is aplied when we want to add specific functionalities to some object instead of the whole class.
Other main feature is that multiple decorators can be stacked on top of each other, adding new functionalities with each new decorator.
The decorator pattern is an alternative to subclassing. Extension adds behavior at compile time, and the change affects all instances; decorating provides new behavior at run-time only for specific objects.
If you already know the Adapter and Proxy you may find them a bit similar. Adapter provides a different interface to its subject and Proxy provides the same interface. Decorator enhaces and object’s responsabilities and is more transparent to the client.
Use cases
The GoF book uses the Window example: we have a very basic GUI windows that works, but the size of the window keeps growing and we need to add some horizontal and vertical scrolling. Here the Decorate patterns fits perfectly. But one of my favorite examples is a character based game where the character evolves as we keep playing. Or a shooting game where you can enhacen your weapon as you gain more experience.
Pros
- Can add functionality to specific objects instead of the whole class.
- Adheres to the Single Responsability Principle, as it allows functionalities to be divided between classes with unique areas of concern.
- Alternative to subclassing.
- Useful to add or remove responsabilities dynamically by simply attaching or detaching decorators.
- Recursive composition, which isn’t possible with pure Adapters
Cons
- Creating too many decorators can make the system hard to maintain and debug.
- Misused can add unnecessary confusion and complexity.
Implementation
For this example we will take the character based game, where the character evolves while we finish missions.
public class CharacterDecoratorShould {
@Test
public void create_character_with_basic_skills() throws Exception {
Character character = new Level1Character();
assertLevelOne(character);
}
@Test
public void upgrade_character_to_level_two() throws Exception {
Character character = new Level1Character();
character = new Level2Character(character);
assertLevelTwo(character);
}
private void assertLevelTwo(Character character) {
assertThat(character.getSkills(), hasSize(2));
assertThat(character.getDamage(), is(25));
}
private void assertLevelOne(Character character) {
assertThat(character.getSkills(), hasSize(1));
assertThat(character.getDamage(), is(10));
}
}
Starting with that very simple test specification for the decorator, our implementation will need a base Character
which will be an interface.
public interface Character {
int getDamage();
List<String> getSkills();
}
And the initial implementation will be the character who the player will start with.
public class Level1Character implements Character {
private final static int INITIAL_DAMAGE = 10;
private List<String> skills = new ArrayList<>();
Level1Character() {
skills.add("Fire control");
}
@Override public int getDamage() {
return INITIAL_DAMAGE;
}
@Override public List<String> getSkills() {
return skills;
}
@Override public String toString() {
return "Level 1";
}
}
But to evolve the Character without loosing progress, we need to create a base Decorator
.
public abstract class CharacterDecorator implements Character {
private final Character decoratedCharacter;
protected CharacterDecorator(Character character) {
this.decoratedCharacter = character;
}
@Override public int getDamage() {
return decoratedCharacter.getDamage();
}
@Override public List<String> getSkills() {
return decoratedCharacter.getSkills();
}
}
Using the above decorator will help us creating new levels for our character and evolve it dynamically.
public class Level2Character extends CharacterDecorator {
private final static int LEVEL_TWO_DAMAGE = 15;
private List<String> levelTwoSkills = new ArrayList<>();
Level2Character(Character character) {
super(character);
levelTwoSkills.add("Water control");
}
@Override public int getDamage() {
return super.getDamage() + LEVEL_TWO_DAMAGE;
}
@Override public List<String> getSkills() {
List<String> initialSkills = super.getSkills();
initialSkills.addAll(levelTwoSkills);
return initialSkills;
}
@Override public String toString() {
return "Level 2";
}
}
A real working example can be the following, where the caracters gets evolved to Level 2.
Character character = new Level1Character();
printInfo(character);
// finish some missions
character = new Level2Character(character);
printInfo(character);
All the code from this post can be downloaded from the Design Patterns repository.
Bibliography
- Agile Principles, Patterns, and Practices in C#
- Java Design Patterns: A tour of 23 gang of four design patterns in Java
- Design patterns in Java training video - Infinite Skills
- Design patterns in Java training video - LiveLessons
- Source Making - Design Patterns
- Design patterns in Java’s core libraries
- Decorator Pattern - Wikipedia
- Decorator Pattern - OODesign
- Is Inheritance Dead? A Detailed Look Into the Decorator Pattern