PDA

View Full Version : Decorator Pattern vs Loop in Coffee Sellling


lolpedia
04-25-2009, 11:27 AM
hi guys, i wanna ask about decorator pattern vs loop.
decorator pattern seems like a class explosion to me.

"head first" uses the coffee example for decorator.

a drink can add a combination of stuff,
and later checks the price.
As a result, it requires to create 1 class for each condiments.
So if there are 50 condiments = 50 class right?

ok...

how about using 1 class that uses switch-case for condiments with price?
This saves up 49 classes...

here is a solution I have in mind:
Condiments List
switch(condimentID)
{
case 1 : price = 10; break;//mocha
case 2 : price = 2; break; //steaming-milk
:
case 50 : price = 8; break; //cream
}

There will be a function that loops again and again till the array completes.
If array has 3 items with id of [1,2,50], it grabs the price from those ID and add up. Isn't that much simpler than Decorator?
However, this method is against the "Open for extension and close for modification" It isn't close since modification is needed on the 1 class to add more stuff in it.

So what do you guys think? The 2nd method is much more efficient or have other thoughts?

maskedMan
04-25-2009, 03:46 PM
What happens when the price of a condiment changes? You then have to go into every coffee class and change variables inside it. What about adding condiments on down the road? It's a more important consideration than you make it out to be. Any time you modify a class you increase the odds that it will have bugs. The more responsibilities you give a class, the more points of failure it can have.

What happens when you want to be able to keep track of exactly where each penny of the cost of the drink comes from? What if multiple condiments cost the same thing but you're interested in keeping track of which condiments were used? Now your coffee has to store references to all of the condiments you gave it.

A class explosion can be bad when they are all redundant or contain redundant information, or if god forbid your superclasses have to change along with your subclasses... Having a large collection of classes is not a bad thing if it is done to keep things flexible and extensible.

lolpedia
04-25-2009, 11:19 PM
What happens when the price of a condiment changes? You then have to go into every coffee class and change variables inside it.
As I mentioned above, all variables for each condiment is in 1 case.
I have already shown an example above...
case 1 : price = 10; break; //mocha
If the prices change, I just need to open up this class that has the
switch-case for all the condiments, use CTRL+F to find that specific
condiment and change the price. What so hard about that?
I do not need to go into "every" coffee class...

Now, you need to ask a question back to yourself.
What if I need to add 1 more function in the abstract component.as?
This leads to a chain reaction whereby
(1) The abstract Decorator.as needs to mirror the functions of abstract component.as and add the override special keyword.
(2) And, the children of decorator [concrete decorator class] needs to override the functions of the superclass.

If there are 50 classes, the maintainability is so much difficult.


What about adding condiments on down the road? It's a more important consideration than you make it out to be. Any time you modify a class you increase the odds that it will have bugs. The more responsibilities you give a class, the more points of failure it can have.
I agree with this, and the concept of "open for extension and close for modifications" If I want to add a condiment, I just place it inside the switch-case. For example: I want to add strawberry into the list,
I simply open up the class, and add 1 more item into the switch list
switch(condimentID){
case 1 : price = 10; break;//mocha
case 2 : price = 2; break; //steaming-milk
:
case 50 : price = 8; break; //cream
case 51 : price = 20; break; //strawberry [NEW - 25/April/2009]
default: break;
}
But how much error could that make?


What happens when you want to be able to keep track of exactly where each penny of the cost of the drink comes from? What if multiple condiments cost the same thing but you're interested in keeping track of which condiments were used? Now your coffee has to store references to all of the condiments you gave it.

If I want the reference, I just check the array...
The array would have the condiments' ID
condimentArray = [1,51,5,50];
It would basically uses 3 array
(1) To keep track what type of drinks - roast, cappuccinos, latte, espresso
(2) To keep track of a condiments for a particular drink - sugar, milk
(3) To store multiple arrays of number 2. - condiment1, condiments2,etc
Then calculate the price.
It only stores the condiments that the client orders,
not all the condiments that is available in the drinking shop.

Now a question for you back, how does Decorator store references to the condiments?
Is it in a string? An array?
From what I know, if it was stored in a string, my system above can also store it in a string, and later break it up apart using .split and place them in array for checking.


A class explosion can be bad when they are all redundant or contain redundant information, or if god forbid your superclasses have to change along with your subclasses... Having a large collection of classes is not a bad thing if it is done to keep things flexible and extensible.
I was wandering whether a swf implementing actionscript 3 and decorator class with more than 200 condiments/items would lag the system?
I have not tried them out, but these are the issues that is worrying me.
(1st issue) - Maintainability of more than 200 classes using decorator
(2nd issue) - Would it lag when 200 classes are linking? Very huge Size?

If these 2 issues are solved, then I would be gladly to use decorator ^_^

wvxvw
04-27-2009, 03:47 PM
Sorry, you're mistaken somewhat about what the decorator pattern is...
Just had a similar conversation in another forum... though, not sure if you're the same person :) So, I'll repeat, kind of :)

Decorator is essentially a class created in run time according to some specific rules. AS3 doesn't have a mechanism for creating classes during run time, not out-of-the-box, that's for sure. Decorator pattern usually requires the programming language to be capable of reflection (i.e. constructing language elements dynamically from some sort of descriptor).
The basic example of reflection would be this:
(pseudo-code)
var toBeClass:XML =
<class name="Foo" extends="MovieClip" implements="IFoo">
<constructorArguments>
<argument type="Array" optional="true" defaultValue="null"/>
</constructorArguments>
<methods>
<doSomething accessor="public" isFinal="false" isStatic="false" returnType="Array">
<arguments>
<argument type="String" optional="false" name="input"/>
</arguments>
<![CDATA[
return input.split(":");
]]>
</doSomething>
</methods>
</class>
which you could use like this:
var FooClass:Class = createClassFromDescriptor(toBeClass);
var fooReflection:IFoo /* you'd be also able to type it to MovieClip */ =
new FooClass([1, 2]);
fooReflection.doSomething("abcd");
This is how you'd create a decorator class in the language that supports reflections.
But, AS3 doesn't support reflections natively. This means that you can write a complex function that will parse the description and compile it into class and then let you use it. But this would be very complex, and, unless you're doing this for some sort of research / library for further extensive use, it will be definitely an overkill for your task.

Flash Gordon
04-29-2009, 08:24 AM
I've been wrong many times before, but as far as I'm aware classes can not be created at run time. Classes are created at author/compile time, objects are created at run time. I do see any benefit of creating classes at run time. Even the xml which would describe a class is created at author (edit: changed from other) time and then at compile time and would still be nothing more than a convenience language like mxml.

Decorator are a way of dynamically adding behavior to an object and having that object "look" the same way but behave (slightly) different. To use decorators in a language requires nothing more than a language which supports polymorphism. It appears lolpedia and maskedman are correct in their use and description of them. And yes, decorator do add overhead: you are daisy chaining several objects together just to get 1 result. However, design patterns and programming is always about trade offs. The benefit here is it is more resistant to change by using decorators. The trade off is performance. However, if you're only checking price once does the performance hit really matter? Probably not unless you are going 3D or games.

And yes, decorator can and often do cause class explosion but it isn't an maintenance issue because your decorators should be closed off. What is open for extension is the object(s) being decorated.

MichaelxxOA
05-02-2009, 06:35 AM
I agree with FG.

Regarding the example: IMO neither of those methods make sense (TBH the example sounds like an illustration). I can't think of where I picked it up, but I use the rule that different data means different objects and different logic means different classes. You have different data (prices?)... probably best put into some kind of data source (text file, xml, database) and creating different objects from it.

wvxvw
05-03-2009, 09:49 PM
Well... you may look here if you want to see an example of creating classes at runtime:
http://etcs.ru/pre/WAVPlayer/srcview/
:)
I've managed to write a class that extended Array and register it at runtime, but cannot find sources right now... and, TBH, that's sort of experiment that consumes a lot of your time and energy and finally pays very little back...
Though, Decorator !== Inheritance. That's just happened so in AS3 we don't have real decorators, so, we use inheritance instead (which is very close, but not the same).

EDIT:
This would be an illustration of what reflection is, and how then you'd create a decorator in JS (I know it's lame :) ) But, think you'll understand.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Reflection example</title>
</head>
<script type="text/javascript">
<!--//
function Foo(arg)
{
this.prop = arg;
}
Foo.prototype.someMethod = function() { return "[object " + this.prop + "]"; }
Foo.prototype.doSomething = function() { alert(this.someMethod()); }
var descriptor = "function Bar(arg){this.prop = arg;};Bar.prototype=Foo.prototype;";
function createFromDescriptor(str)
{
eval(str);
var className = str.match(/^function\s*(\w+)/)[1];
var func = eval(className);
return new func(className);
}
function testReflection()
{
var bar = createFromDescriptor(descriptor);
bar.doSomething();
}
//-->
</script>
<body onload="testReflection()">
</body>
</html>

web developer
02-05-2012, 04:32 PM
The coffee example in Head First is basically an array or list. You could of had a CoffeeThing class which Espresso, HouseBlend, Soy, Mocha, etc. inherit from. Each overrides the cost() method. You put all the CoffeeThings in an array/list, and add everything together to get the cost.
One problem with the above is when you add the size of the drink into consideration, which is one of their problems. With the decorator pattern, the size of the drink only needs to be in the first object you decorate. All the other objects that wrap first object also get the size of the drink. If you do it the way I've described, you'd do something like:

var list:Vector<CoffeeThing> = new Vector<CoffeeThing>;
list.push(new Espresso(large));
list.push(new Soy(large));
...
// or use setSize()


This could cause maintainability problems.

drkstr
02-07-2012, 11:43 PM
Poor example for the decorator pattern. You essentially just described a relativly simple "data operation". Operations on a data structure can be fine tuned and optimized as needed in a variety of ways. This has little to do with OOP.

A decerator is used more for something like Aspect-Oriented Programming (http://en.wikipedia.org/wiki/Aspect-oriented_programming), where you can "assign" functionality to a class based on some pre-determined abstract concepts. A perfect example of a decorator is the various layout (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/layouts/package-detail.html) and effect (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/effects/package-detail.html) classes you can give to spark (Flex) components to describe their behavior under certain conditions.

You are correct that writing 50 classes to preform a simple value lookup over a fixed list of options is a terrible idea (although I probably would not go with a switch statement either... but that is more of a personal preference...) .

*edit*

Also...

There will be a function that loops again and again till the array completes.
This sounds very dicey.

I would go with something more like this:

public class PriceCalculator
{
private static const PRICE_TABLE:Object = {
mocha : 3.00,
steamingMilk : 2.50,
cream : 1.50
};

//this could preform a variety of price calculations given a set of options...
public function getPrice(name:String):Number
{
return Number(PRICE_TABLE[name]);
}
}

The idea of which can be expanded or optimized in a variety of ways...

drkstr
02-08-2012, 04:05 AM
I've managed to write a class that extended Array and register it at runtime, but cannot find sources right now... and, TBH, that's sort of experiment that consumes a lot of your time and energy and finally pays very little back...
It's much easier to compile the class definition server-side (Flex Compiler API (http://blogs.adobe.com/flexdoc/files/flexdoc/oem/oem_guide.pdf)), then inject it into the application domain with some kind of Loader. :P

GraniteDS+Jetty+ByteArray works beautifully

Thems the breaks for iOS or offline web apps though...

wvxvw
02-08-2012, 08:18 AM
drkstr - this discussion originally happened almost 3 years ago :) But to answer that - nope, for the general use Flex compiler is tremendously slow, so compiling classes serverside is out of question (possible but not scalable).

drkstr
02-08-2012, 03:54 PM
Haha, I never look at the original thread dates! Gets me every time...

You are right, it is a bit slow, but it works well for a "publishing" type of set up where class definitions can be compiled and reloaded as part of the publishing processes. The compiler isn't even our biggest bottleneck in the set up.

wvxvw
02-08-2012, 09:24 PM
Why am I not surprised to hear there are things in Java more slow than MXMLC :D But yeah, this really depends, if you are making something like wonderfl, then it doesn't really matter that much (you can afford to wait seconds for compilation), buf if you need that to serve content at the same speed a regular web server would serve HTML pages, that's light years more slow...

zarmeda
11-08-2013, 11:30 AM
gooooooooooooooooood very very good :o:D:D