Decorator¶
The decorator is a structural pattern whose main job is to add extra functionality to an existing object, without modifying the underlying behavior.
Construction¶
The decorator adds functionality without applying the inheritance mechanism and enables dynamic enrichment of functionality (i.e. during the runtime). It is based on a common interface (or base class) that wraps the decorated object that also implements it.
By using a common interface, it is possible to use many decorators at once, in any order.
Example¶
The following example consists of the following parts:
- the common interface
FragStatistics
FirstPersonShooterFragStatistics
- the base class to be decorated- four decorators:
DeathCountInfoDecorator
,DisplayCountersDecorator
,FragDeathRatioDecorator
,FragInfoDecorator
- the class
DecoratorUsage
showing the use of this pattern.
public interface FragStatistics {
int incrementFragCount();
int incrementDeathCount();
void reset();
}
public class FirstPersonShooterFragStatistics implements FragStatistics {
private int fragsCount = 0;
private int deathCount = 0;
@Override
public int incrementFragCount() {
fragsCount++;
return fragsCount;
}
@Override
public int incrementDeathCount() {
deathCount++;
return deathCount;
}
@Override
public void reset() {
fragsCount = 0;
deathCount = 0;
}
}
public class DeathCountInfoDecorator implements FragStatistics {
private final FragStatistics fragStatistics;
public DeathCountInfoDecorator(final FragStatistics fragStatistics) {
this.fragStatistics = fragStatistics;
}
@Override
public int incrementFragCount() {
return fragStatistics.incrementFragCount();
}
@Override
public int incrementDeathCount() {
System.out.println("Fragged by an enemy");
return fragStatistics.incrementDeathCount();
}
@Override
public void reset() {
fragStatistics.reset();
}
}
public class DisplayCountersDecorator implements FragStatistics {
private final FragStatistics fragStatistics;
public DisplayCountersDecorator(final FragStatistics fragStatistics) {
this.fragStatistics = fragStatistics;
}
@Override
public int incrementFragCount() {
int fragCount = fragStatistics.incrementFragCount();
System.out.println("Your frag count is now " + fragCount);
return fragCount;
}
@Override
public int incrementDeathCount() {
final int deathCount = fragStatistics.incrementDeathCount();
System.out.println("Your death count is now " + deathCount);
return deathCount;
}
@Override
public void reset() {
fragStatistics.reset();
System.out.println("Stats reset - KDR is equal to 0");
}
}
import static java.util.Objects.nonNull;
public class FragDeathRatioDecorator implements FragStatistics {
private final FragStatistics fragStatistics;
private Integer currentFragCount = null;
private Integer currentDeathCount = null;
public FragDeathRatioDecorator(final FragStatistics fragStatistics) {
this.fragStatistics = fragStatistics;
}
@Override
public int incrementFragCount() {
currentFragCount = fragStatistics.incrementFragCount();
displayFragDeathsRation();
return currentFragCount;
}
@Override
public int incrementDeathCount() {
currentDeathCount = fragStatistics.incrementDeathCount();
displayFragDeathsRation();
return currentDeathCount;
}
private void displayFragDeathsRation() {
if (nonNull(currentFragCount) && nonNull(currentDeathCount)) {
System.out.println("KDR is " + (double) currentFragCount / currentDeathCount);
}
}
@Override
public void reset() {
fragStatistics.reset();
}
}
public class FragInfoDecorator implements FragStatistics {
private final FragStatistics fragStatistics;
public FragInfoDecorator(final FragStatistics fragStatistics) {
this.fragStatistics = fragStatistics;
}
@Override
public int incrementFragCount() {
System.out.println("Enemy fragged");
return fragStatistics.incrementFragCount();
}
@Override
public int incrementDeathCount() {
return fragStatistics.incrementDeathCount();
}
@Override
public void reset() {
fragStatistics.reset();
}
}
public class DecoratorUsage {
public static void main(String[] args) {
FragStatistics statistics = new FirstPersonShooterFragStatistics();
statistics.incrementDeathCount(); // nothing appears on the screen
statistics.incrementFragCount(); // nothing appears on the screen
// use of decorators
FragStatistics decoratedStatistics = new FragDeathRatioDecorator(new FragInfoDecorator(new DisplayCountersDecorator(new DeathCountInfoDecorator(statistics))));
decoratedStatistics.incrementFragCount();
decoratedStatistics.incrementFragCount();
decoratedStatistics.incrementFragCount(); // because we are good players!
decoratedStatistics.incrementDeathCount();
}
}
After execution of the main
method, the following output will be displayed:
Enemy fragged
Your frag count is now 2
Enemy fragged
Your frag count is now 3
Enemy fragged
Your frag count is now 4
Fragged by an enemy
Your death count is now 2
KDR is 2.0
Note that the order of the decorator classes is of little importance to the functionality of the application. Moreover, the use of many decorators may require multiple use of the new
operator.