Posted 08 October 2010 - 12:10 PM
Interesting point. Here's my take on it.
As you may have noticed, the code works if one derives the condiments from Beverage. However, in the case of the Decorator pattern, inheritance is being used for type matching. This is not the typical reason for sub-classing. The normal reason for sub-classing is to inherit behavior
To make this distinction clear, it makes sense to sub-class Beverage with CondimentDecorator because the CondimentDecorator class makes the distinction between a drink implementation (like DarkRoast) and condiment (like Mocha) clear. Consider for example you were tasked to come up with a menu for StarBuzz just by looking at the code. You would immediately know which are "base" beverages and which condiments by looking as the base class. DarkRoast's base class is Beverage. Mocha's base class is CondimentDecorator.
Still, I think it might make more sense in Java to implement the Beverage abstract class as an interface instead. According to the book, the code didn't use this approach because StarBuzz already had an abstract Beverage class (p 93) and an abstract base component is the historical approach to implementing the pattern.
As for the ArrayList implementation, it would work but it does not as effectively meet the criteria for Open for Extension, Closed for Modification. Imagine if you will a change to the design where StarBuzz would like to add different beverage containers (paper, paper with sleeve and customer provided). The new functionality would be implemented in a manner similar to CondimentDecorator class. In this case, each new class would derive from a "CupDecorator" base class.
In the Decorator Pattern implementation, no change would be required to the Beverage class.
However, with an ArrayList implementation, there would be a need to modify the Beverage class to add a new ArrayList and a new method to handle adding the "CupDecorator" instances.