Hi, I have a question about an example given in the book named above. In chapter 3: Decorator Pattern, a problem is given about a StarBuzz coffee application.We have an existing code that caters to around 4 basic beverages and now we want to apply different condiments (Milk,Mocha,Whip etc..) to it. Question: Instead of Decorator pattern, can't we make a Condiments interface seperately and have all Mocha, Milk,Whip etc. condiments implement it and then define their own cost() methods? Next, we keep a List of all these condiments in the Beverage class (Aggregation) and to calculate the total cost, do something like the following pseudocode:
If anybody who has read this book and understood can spare some time, I shall be greatful.
Joined: Jul 22, 2008
Second call for help, please! May be the authors can spare some time?
Yes that is a possibility. As long as that interface is already being used for Condiments. However, is whip a condiment. To me it sounds like an action.
And also note that the example in the book is to show Decorator, so they are only going to show Decorator, it never means that there aren't other possible solutions, just that this is an example that demonstrates Decorator, no matter how contrived something could be.
Thanks Mark. Actually, I was taking a view that the examples given in the book are such that the pattern being explained is supposed to be the best fit for a given problem. Specially because in the book other approaches are considered and rejected for their demerits.
The solution that you have provided will mandate that the beverage classes need to change. isnt? That is one of the things, according to the problem statement, the implementor does not want to do.
Conceptually, a simple coffee does not need to know what add ons it is being served with. It will be the responsibility of someone who is making the coffee to add on the condiments(or to "decorate" the coffee with the condiments).
The solution that you have provided will mandate that the beverage classes need to change. isnt?
Ok, I "misspoke" about the List of condiments being maintained by the Beverage class.Its election season in the US you see, so I should be pardoned :-)
Let me propose a solution slightly different from the one earlier. Say we have a third class called Drink. Drink will have a Beverage and a list of Condiments. The pseudocode is the same except that it now resides in the Drink class. So, now, we have not changed the Beverage class and we have a list of Condiments as well. Beat that.
aditee: Say we have a third class called Drink. Drink will have a Beverage and a list of Condiments.
Is Drink an interface or an implementation? If it is an implementation, what interface does it implement?
If it is an interface, then as a coffee maker I have to know 2 interfaces, one for Plain Beverages and other for Drink.
If the interface of Drink is the same as beverage, then probably without knowing you are using the decorator pattern
Joined: Jul 22, 2008
Is Drink an interface or an implementation?
At the risk of sounding stupid, let me say : Neither. Its just a calling class. So, if a customer orders a beverage (Drink is the client class), we create a new instance of that beverage and get its cost. Next we maintain a List<Condiments> and keep adding to this list all the condiments that the customer orders. Next, iterate through this list and keep adding the costs to the beverage cost, and you get the total.
Well, may be I've been thinking too much and said something stupid, having totally lost my way...But I need to get this concept home once and for all.Kindly bear with me.
I would not comment on the name of the classes(Drink in particular).
I assume in the following statement:
Next we maintain a List and keep adding to this list all the condiments that the customer orders.
"We" is the StarBuzz coffee application?
If yes, then now the coffee application has the logic of calculating the price. In the initial design, the disposed entity(beverage) has the logic of calculating its own price. The condiments know that they have to add their price to the one obtained from super() and the basic beverages know their own prices.
Additionally, the design must also depend on what comes out of the coffee application. If the whole intention is to calculate the price then probably you can use your design. But the intention is to dispense the beverage. In your design, the client will get a beverage and has to call some other method to get the cost. (correct me if i am wrong) In the initial design, the only object that one knows is the beverage. It may be used to calculate cost or to drink!
Having, said the above, there is no such thing as a perfect design. Different people think of things in different ways. Design patterns are present because they identify "most common solutions to common problems". Your solution may not be the "most common" but it still is a solution
Joined: Jul 22, 2008
"We" is the StarBuzz coffee application?
Yes, or the Drink class that I named.Lets call it StarBuzz from now onwards.
If yes, then now the coffee application has the logic of calculating the price. In the initial design, the disposed entity(beverage) has the logic of calculating its own price.
If by "own" price, you mean just the beverage price(i.e without condiments), then the logic still resides inside the Beverage class, StarBuzz is just calling it to calculate the total cost.
In the original design, the condiments know that they have to add their price to the one obtained from super() and the basic beverages know their own prices.
That means strong coupling between condiments and Beverage.It may be desirable business-wise, but may be not from a coding point of view.
In your design, the client will get a beverage and has to call some other method to get the cost.
Well, the client will get the cost from the beverage only, and to calculate the total cost, it will iterate over the list of condiments and add them to the beverage-only cost.
In the initial design, the only object that one knows is the beverage.
Yeah, we create only Berverage reference though we are assigning Condiment Objects to it many times. Is that a requirement to know of only one Class Reference ?
aditee: If by "own" price, you mean just the beverage price(i.e without condiments)
No, "own" price means price of anything disposed by the coffee application. Beverage or Beverage + condiments, whatever.
aditee: That means strong coupling between condiments and Beverage.
I beg to differ. Strong coupling will be if i keep condiments inside the beverage. This means beverage will always know the condiments. If i decorate beverage with condiments then beverage does not know condiments and condiments only knows that it is supposed to be always served with "something". I would not like to call it strong coupling because that is the nature of condiments. They are never served alone but always as an add-on.
aditee: Yeah, we create only Berverage reference though we are assigning Condiment Objects to it many times.
Now, you are confusing me.
Let me propose a solution slightly different from the one earlier. Say we have a third class called Drink. Drink will have a Beverage and a list of Condiments.
I thought you are saying that the starbuzz coffee application creates one more object that is a composition of beverage and a list of condiments.
aditee: Is that a requirement to know of only one Class Reference ?
Ok i change my initial statement a little. What i meant was:
the only *interface* that one knows is the beverage.
Now instead of the pseudo code can you put in the real code and let us discuss from there.
In your implementation you have changed the Beverage class to contain the condiments. This is something that does not seem correct. Beverages in my opinion should not know condiments.
Now, if you make one more class, which is a composite of condiments and beverage(this is what i thought you were saying) then it will have one method cost(). Now, is there a difference between the contract(interface) of the new composite class and the old beverage class? I think no, because they just have one method cost() If there is no difference why do you want to define a new interface?
If it is the same interface then you are using the decorator pattern.