Take the 2-minute tour ×

How to avoid duplicates for certain classes in a Decorator chain?

Long version
I'm trying to implement the Decorator pattern as described in the book Head First Design Patterns. The example they give is a cash register for a Starbucks-like coffee store, where the beverages and the various condiments are classes, and the purpose of the system is to calculate the price.

I started with an iBeverage interface, exposing the public method getName() and getPrice().

Implementations of iBeverage are the various beverage classes (though a shared baseclass called Beverage; these are SlowDripCoffee, Latte and Cappuccino), and various condiments.

The condiments are implemented as iBeverageDecorators (an interface extending iBeverage). In their constructor they accept an instance of iBeverage, which is stored internally. Calls to getName() and getPrice() are implemented as "call getPrice() on child, add your own price, return the value."

Example of how one would create a Tall Soy Caramel Latte:

 $drink = new SoyMilk(new CaramelSyrup(new Latte(Beverage::TALL)));
 $price = $drink->getPrice();`

My issue: How can I restrict - for some decorators only - the total amount of times they end up in the call stack? For example: what if the store wants to restrict syrups to three per beverage, or they want to make sure employees only add one type of milk to an order?

My basic instinct was to add some reflection, so that during construction we can examine the entire object graph and determine whether or not it is a valid target for the decorator... is this the right way to go? Or is the pattern I'm applying here not suitable for this task? Any advice is much appreciated.

This is just something I set out for myself to do and find out.

share|improve this question
Yes. Because with this design sugar will be a beverage too. –  hoha Mar 23 '11 at 17:52
Hoha, I disagree, because you can't order "sugar". What you can order is a drink with sugar in it - hence it is still a beverage. –  kander Mar 24 '11 at 7:53
If I understand this design correctly - Sugar implements IBeverageDecorator with IBeverageDecorator extends IBeverage. Therefore sugar is a beverage here. BTW my point is that simple aggregation of condiments in a beverage will solve the whole problem easily without all this patternspeak ("beverage decorator", oh my) and without dirty hacks required to do simple things (optional condiments limit, and uniqueness checks and condiment compatibility checks etc.). Let's also think about DB representation - Beverage "one-to-many" Condiment is quite natural. –  hoha Mar 24 '11 at 10:10
add comment

1 Answer

Normally this should be an issue, you build your chain either manually (you hard coded it) or you have a builder that adds the decorators to the chain where you can control this.

I wouldn't put any code for this in the decorator classes itself because then you are spreading the rules about this all over the place and making this inflexible and hard to maintain.

The example you used is all about being able to cope with changed rules and ingredients.

share|improve this answer
add comment

Your Answer


