Decorator (Wrapper - Smart Proxy) Pattern
- is a structural design pattern that adds behavior to an object dynamically without modifying its class
Code Example
Coffee Example (Simple Example)
interface Coffee { double cost(); } class SimpleCoffee implements Coffee { public double cost() { return 5.0; } } class MilkDecorator implements Coffee { private final Coffee coffee; MilkDecorator(Coffee coffee) { this.coffee = coffee; } public double cost() { return coffee.cost() + 1.5; } }Using it
Coffee coffee = new MilkDecorator(new SimpleCoffee()); System.out.println(coffee.cost()); // 6.5
Troll Example (Complex Example)
Let’s take the troll example. First of all, we have a
SimpleTrollimplementing theTrollinterface:public interface Troll { void attack(); int getAttackPower(); void fleeBattle(); } @Slf4j public class SimpleTroll implements Troll { @Override public void attack() { LOGGER.info("The troll tries to grab you!"); } @Override public int getAttackPower() { return 10; } @Override public void fleeBattle() { LOGGER.info("The troll shrieks in horror and runs away!"); } }Next, we want to add a club for the troll. We can do it dynamically by using a decorator:
@Slf4j public class ClubbedTroll implements Troll { private final Troll decorated; public ClubbedTroll(Troll decorated) { this.decorated = decorated; } @Override public void attack() { decorated.attack(); LOGGER.info("The troll swings at you with a club!"); } @Override public int getAttackPower() { return decorated.getAttackPower() + 10; } @Override public void fleeBattle() { decorated.fleeBattle(); } }Here’s the troll in action:
// simple troll LOGGER.info("A simple looking troll approaches."); var troll = new SimpleTroll(); troll.attack(); troll.fleeBattle(); LOGGER.info("Simple troll power: {}.\n", troll.getAttackPower()); // change the behavior of the simple troll by adding a decorator LOGGER.info("A troll with huge club surprises you."); var clubbedTroll = new ClubbedTroll(troll); clubbedTroll.attack(); clubbedTroll.fleeBattle(); LOGGER.info("Clubbed troll power: {}.\n", clubbedTroll.getAttackPower());Program output:
A simple looking troll approaches. The troll tries to grab you! The troll shrieks in horror and runs away! Simple troll power: 10. A troll with huge club surprises you. The troll tries to grab you! The troll swings at you with a club! The troll shrieks in horror and runs away! Clubbed troll power: 20.
Code Structure
|
|
Applicability
Decorator is used to:
- Add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects.
- For responsibilities that can be withdrawn.
- When extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing.
Comparisons
Click here to expand...
Link to originalAdapter Pattern vs Decorator Pattern
- main difference is that:
- the decorator pattern is used to add functionality to an object at run-time without changing the object’s method signature(s)
- the adapter pattern is used to change the interface of an object to adapt it to another interface (i.e. different method signature(s))
- The decorator pattern is similar to the adapter pattern in that one service “wraps” another. However, in contrast to adapters, decorators expose the same service as what they’re decorating.
Link to originalChain of Responsibility Pattern and Decorator Pattern have very similar class structures. Both patterns rely on recursive composition to pass the execution through a series of objects. However, there are several crucial differences.
- The CoR handlers can execute arbitrary operations independently of each other. They can also stop passing the request further at any point. On the other hand, various Decorators can extend the object’s behavior while keeping it consistent with the base interface. In addition, decorators aren’t allowed to break the flow of the request.
Link to originalDecorator Pattern vs Proxy Pattern
- Decorator and Proxy have similar structures / same interface as their wrapped types, but different intent
- Proxy creates an instance under the hood, Decorator takes an instance in the constructor
- Differences between Proxy and Decorator Pattern
/structural-design-patterns/wrapper-patterns/decorator-(wrapper---smart-proxy)-pattern/structure-indexed-2x.png)