Attach additioinal responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
- Responsibilities should be added to (and removed from) an object dynamically at run-time.
- A flexible alternative to subclassing for extending functionality should be provided.
Define Decorator objects that
- implement the interface of the extended (decorated) object (
Component) transparently by forwarding all requests to it and - perform additional functionality before/after forwarding a request.
- Component (Cake)
- defines the interface for objects that can have responsibilities added to them dynamically.
- ConcreteComponent (MuffinCake)
- defines an object to which additional responsibilities can be attached.
- Decorator (CakeDecorator)
- maintains a reference to a Component object and defines an interface that conforms to Component's interface.
- ConcreteDecorator (WhippedCream)
- adds responsibilities to the component.
Decorator forwards requests to its Component object. It may optionally perform additional operations before and after forwarding the request.
- provide a flexible alternative to subclassing for extending functionality.
- allow behavior modification of an object dynamically at run-time.
- can result in many small objects and overuse can be complex.
- can cause issues if the client relies heavily on the components concrete type.
- A decorator and its component aren't identical.
- A decorator acts a transparant enclosure. From an object identity point of view, a decorated component is not identical to the component itself. Hence you shouldn't rely on object identity when you use decorators.
See complete Cake Factory with Decorator Pattern in Java
Definition
Component
publicabstractclassCake {
protectedStringdescription = "cake";
privateLocalDatecreatedDate;
publicCake() {
this.createdDate = LocalDate.now();
}
publicStringgetDescription() {
returndescription;
}
publicfinaldoublecost() {
LocalDatetwoDaysAgo = LocalDate.now().minus(2, ChronoUnit.DAYS);
doublebaseCost = baseCost();
returncreatedDate.isBefore(twoDaysAgo) ? baseCost * 0.8 : baseCost;
}
publicvoidsetCreatedDate(LocalDatecreatedDate) {
this.createdDate = createdDate;
}
protectedabstractdoublebaseCost();
}ConcreteComponent
publicclassMuffinCakeextendsCake {
protectedStringdescription = "6-set bilberry muffin";
@OverridepublicStringgetDescription() {
returnString.format("%s %s", this.description, super.getDescription());
}
@OverridepublicdoublebaseCost() {
return19;
}
}Decorator
publicabstractclassCakeDecoratorextendsCake {
protectedCakeinnerCake;
protectedStringdescription;
protecteddoublecost;
publicCakeDecorator(Cakecake, Stringdescription, doublecost) {
this.innerCake = cake;
this.description = description;
this.cost = cost;
}
@OverridepublicStringgetDescription() {
returnString.format("%s, %s", innerCake.getDescription(), this.description);
}
@OverridepublicdoublebaseCost() {
returnthis.cost + innerCake.baseCost();
}
protected <T> booleanisInnerCake(Class<T> type) {
Cakecake = innerCake;
System.out.println(cake.getClass());
do {
if (cakeinstanceofCakeDecorator) {
cake = ((CakeDecorator) cake).innerCake;
} elseif (cake.getClass().equals(type)) {
returntrue;
} else {
returnfalse;
}
} while (cake != null);
returnfalse;
}
}ConcreteDecorator
publicclassWhippedCreamextendsCakeDecorator {
publicWhippedCream(Cakecake) {
super(cake, "Whipped Cream", 4.5);
}
}Usage
Cakecake = newMuffinCake(); // 19cake = newWhippedCream(cake); // 4.50cake = newChocolateSprinkles(cake); // 2.50cake = newTextWriting(cake, "Anniversary"); // 8assertEquals("muffin cake description",
"6-set bilberry muffin cake, Whipped Cream, Chocolate Sprinkles, Anniversary with ink on a card",
cake.getDescription());
assertEquals(cake.getDescription(), 34, cake.cost(), 0);- Adapter: A decorator is different from an adapter in that a decorator only changes an object's responsibilities, not its interface; an adapter will give an object a completely new interface.
- Composite: A decorator can be viewed as a degenerate composite with only one component. However, a decorator adds additional responsibilities - it isn't intended for object aggregation.
- Strategy: A decorator lets you change the skin of an object; a strategy lets you change the guts. These are two alternative ways of changing an object.

