Re: Announcing Head First Design Patterns
Posted: Nov 16, 2004 12:54 PM
I read the chapter at the O'Reilly website.
Very nice way of explaining things. I like the conversational tone of explanation, mainly because it enlivens the exchange. To the reader it almost seems as if he is having a conversation with the author.
The following is only with reference to Chapter 3 of the book available at the O'Reilly website.
I do have something to say about the use of the Decorator pattern. I feel that even with the Decorator pattern, there are far too many classes. Also, addition of a new Beverage requires adding a lot of code. Adding new features such as discounts and sizes to the problem is non-trivial. There is a lot of repetitive code.
Let us look at the problem from a user perspective, the person behind the counter who is taking orders. Here is what I think happens.
A customer walks in and says, for example, "I want a House Blend with mocha." The cashier enters the information and expects a price to appear on the screen.
The information needed by the system for calculating the price are:
1. The name of a beverage (House Blend, decaf, etc.), and its size.
2. The names of zero or more condiments.
3. The discounts applicable to the beverage.
This leads me to a process/class called "MakeBrew" that has all this information and is able to accept the information and calculate a cost.
Starting with a top-down (XP practitioners faint in horror here!) user-centric design I'd create a Brew class that accepts an instance of a Beverage Class and a BeverageSize (possibly an Enumeration). We add Condiment[s] to it, and then calculate the cost. Beverage Discounts can be applied at the end. The cost, of course, depends on the BeverageSize.
This leads us to a list of classes: Brew, Beverage, BeverageSize, BeverageDiscount and Condiment.
Looking at the Beverage class, the question arises about whether we should create sub-classes for Espresso, Decaf, etc. Creating sub-classes does help. But a deeper look at the various possible subclasses show that the key feature is that each Beverage has a description, size, cost and a discount.
We can create sub-classes for each Beverage. But if a new Beverage is added, we need to create a new sub-class, having the same attributes as all other sub-classes of Beverage. Is there an alternative?
Given that the only differences between beverages are the values of various attributes such as cost, discount, etc., I prefer a "data-driven" approach, where we can pick up the information from a data source (XML file, properties file, text file, or a database). This data source is used to create all instances of the Beverage class. The Brew class uses instances of these pre-created Beverages, adds Condiment[s] (defined similar to Beverage, with no sub-classes and instances obtained from a data source), and then computes the cost based on the size and applicable discounts.
The advantage of this design is that, adding a new Beverage simply means adding new data to the data source. No code change or addition is needed to add a new Beverage.
Likewise, adding a new Condiment is simply a matter of adding data, requiring no code changes. Changing the cost of a beverage or a condiment is simple, requiring no code change.
In essence, what we see is that a different design requires no inheritance at all, and is simpler and easier to maintain and enhance.
Incidentally, in this particular example there is no need for the decorator pattern and its sub-classes.
When reading the chapter, I saw the requirement that the Decorator class have the same interface as the Beverage class as a sign that an alternative ought to be available. From a design perspective, a decorator should be using composition, not inheritance, to add behaviour. The Brew class described here serves the same purpose as the Decorator class, uses composition, enforces the restriction that there must be exactly one Beverage in the Brew (the only constructor requires a Beverage and BeverageSize), and makes it easy to add discounts or account for size. The decorator pattern forces the developer/designer to create sub-classes of the Decorator class, yet makes it difficult to add meaningful functionality. (Adding a new size would require adding the size to all the sub-classes, repetitive work that is not aesthetically pleasing.)
In general, I find that I seldom use inheritance in my OO work.
If anybody is interested, I can post the source code for the example.