Design Patterns: The Decorator Pattern in Java
by Riley MacDonald, August 9, 2017

The decorator pattern is a handy way to close modification of existing code while encouraging extension of classes. The decorator pattern also allows additional responsibilities to be added to an object dynamically at runtime as opposed to statically during compile time. Maintenance of large APIs (or portions of them) becomes much easier when using this pattern.

This post will use a point of sale (POS) system that records sandwich sales as an example. The POS calculates sandwich prices based on the type of bread and ingredients used (defined by the sandwich subclasses / customer order).

Subclassing example problem:
Here’s an example Sandwich object and some of its subclasses.

// Sandwich superclass object
public abstract class Sandwich {
    String name;
 
    boolean hasMustard;
    boolean hasMayonaise;
    boolean hasLettuce;
    // ...
 
    public abstract String getName();
    public abstract double cost();
 
    // ... getters / setters
}
 
// Sandwich subclasses
public class WholeWheat extends Sandwich {
    public double totalCost = 5.99;
    @Override public double cost() { 
        if (hasLettuce) {
            totalCost += 0.10;
        }
        // ... and so on
    }
}
 
public class Wrap extends Sandwich {
    @Override public double cost() { //...  }
}

What happens when someone orders a BLT with double bacon? Create a new double bacon instance variable? With all the possibilities of sandwiches people could choose to order, the total list of classes and variables will eventually grow out of control. What happens when a new ingredient is added? Every one of the Sandwich subclasses will need to be modified, a maintenance nightmare!

Utilizing the Decorator Pattern
This scenario is a good candidate for the Decorator Pattern. This pattern is implemented by “wrapping” decorator objects of the same type. To implement this we’ll need an abstract superclass, abstract decorator, and a few subclasses. Let’s make the necessary changes to implement the decorator pattern to the above Sandwich example.

Abstract Component Class
We already know Sandwich is the abstract superclass. Let’s simplify it.

// The sandwich superclass after refactoring
public abstract class Sandwich {
    String name;
 
    public abstract String getName();
    public abstract double cost();	
}

Concrete Sandwich Components
For this example sandwiches can only have one type of bread (eg. Whole wheat, wrap, white, etc.). These are the concrete sandwich components.

public class WholeWheat extends Sandwich {
    public WholeWheat() {
	// overriden from Sandwich	
        description = "Whole wheat";
    }
 
    @Override
    public double cost() {
        return 4.00
    }
}

Ingredient Decorator
This abstract decorator represents all the ingredients a sandwich can have.

public abstract class IngredientDecorator extends Sandwich {
    public abstract String getName();	
}

Ingredient Decorators Subclasses
Each ingredient will have it’s own decorator. Here’s an example of one of the ingredient decorators.

public class Ham extends IngredientDecorator {
    Sandwich sandwich;
 
    public Ham(Sandwich sandwich) {
        this.sandwich = sandwich;
    }
 
    @Override
    public String getName() {
        return sandwich.getName() + ", Ham";
    }
 
    @Override
    public double cost() {
        return sandwich.cost() + 1.20;
    }
}

Implementation Example
Here’s everything in action!

public class PointOfSaleMachine {
    public static void main(String args[]) {
        Sandwich sandwich = new WholeWheat();
 
        // Build up the sandwich using the decorator pattern
        sandwich = new Ham(sandwich);
        sandwich = new Pickle(sandwich);
        sandwich = new Ketchup(sandwich);
 
        // Print out the details
        System.out.println(String.format("Order detail for: %s. Total cost: %s.", sandwich.getName(), sandwich.cost());
	}
}

Result
Show the result of the sandwich output.
$ > Order detail for: Whole wheat, Ham, Pickles, Ketchup. Total cost: $5.35.

Summary
The decorator pattern is powerful for this type of scenario. However decorator pattern APIs can be a bit daunting to new or unfamiliar developers. “Reassigning” (wrapping) the same object by passing itself isn’t exactly readable but it pays off. Anytime an ingredient is modified or added the changes are encapsulated to a single class. The existing Sandwich class remains unchanged and Clients utilizing the API should also see minimal changes.

Open the comment form

Leave a comment:

Comments will be reviewed before they are posted.

User Comments:

Be the first to leave a comment on this post!