Skip to content

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.

decorator

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.