This is part of a series of recommendations on how to implement some of the famous Gang of Four OOP design patterns using LVOOP. It represents an open discussion with the NI Community, so please feel free to provide comments and suggestions if you have any!
Extend a concrete object’s responsibilities dynamically. Extend the features of an existing class without refactoring tested code.
When refactoring an application to add new features, it’s best to avoid digging into code that has already been tested. This pattern provides a means of extending an object’s functionality without changing the original class’s behavior. The pattern also solves the problem of defining a class whose objects may have any permutation of a set of responsibilities. It creates a scalable and recursive mechanism for defining that set and expanding it in the future.
The attached example uses a coffee store for illustration: you could order your coffee black, or you could want to add double mocha and whipped cream to it. Instead of defining a set of n! subclasses to account for every possible type of drink, you can just define one subclass for each drink and condiment. The subclasses each contain one another and share the same list of methods as one another. When a method is called on an object, that object first calls the same method on its contained object. Then it appends that method’s output with its own data and outputs to the client.
This application can also be viewed as a feature addition to the coffee store after its release. The original coffee store sold simple coffee in four types:
Later, condiments were added. The condiments are also children of Beverage.lvclass, so they take the same data type as the original drink classes (Espresso.lvclass, etc.):
The condiments also have “Cost.vi” and “Get Description.vi” as dynamic dispatch methods, and they contain a single Beverage object as a data element. When a drink like Espresso is instantiated, it can be wrapped up (decorated) by a condiment like Whip. Then the client calls Whip.lvclass:Cost.vi to get the total cost of an espresso with whipped cream. This provides run-time feature extension of the basic drink classes, as well as a scalable means of defining new features with future releases of the software.
[David Staab] This specific example was converted from the Java code provided in Head First Design Patterns, Eric Freeman et. al. 2004. O'Reilly Media, Inc. See the Gang-of-Four text for an example using UI components instead of a world model.
NOTE: "DecoratorPattern_Revised_2013.zip" is a modified version of the first version that cleans up several oddities in the first version. The revised version avoids a whole bunch of replicated strings for the Description and changes how the Description gets generated so that the formatting is not left to each individual class. In the revised version, compare how the Condiment Decorator class computes the Description versus how it generates the Cost. These are two different common techniques for decorating an attribute. The Description is done by having a single method on the Condiment Decorator class that specifies how to reach inside the contained class. The Cost is done by having each class decide how to modify the cost. The second method is appropriate if you were going to have, for example, condiments that multiplied the overall price instead of just adding a value.
I've tried to extend this example to include size as outlined in chapter three of the book (http://oreilly.com/catalog/hfdesignpat/chapter/ch03.pdf)
I've successfully added a typdef to the Beverage.lvclass private data. I can create Set and Get methods to configure the Size of the beverage, and I've modified the Get Description VI to prepend the configured size of the beverage in the description string.
I don't understand how to use the Size value from within the child class to set the condiment cost based on beverage size.
(Using LabVIEW 8.6.1f1)
You did a really good job with this. I like this pattern a lot because of the run time flexibility but I wonder how often I would actually use it. Have you implemented it in any real world scenarios yet?
TheMadScientist wrote:
Have you implemented it in any real world scenarios yet?
Many, both in LV and other languages. Design patterns should be observed in real code and then documented, not proposed and then adopted into real code. The original patterns were found by observing the practices of many applications that people felt were well written. I try to discourage people from posting patterns until they are vetted. David did a great job with this one, and it is definitely in the "been there done that" category.
@AristosQueue could you give an example of a situation in which you would want to use the decorator?
For the above example dealing with coffee he could just as easily have made an array of condiments an internal member variable of the Coffee class; Mocha, milk, and sugar could be made of type Condiment. Condiment would be an abstract base class. The coffee class would have a public method AddCondiment(Condiment _condiment) that would be called at run time.This would preserve the real world "has a" relationship between coffee and its condiments. For example in the following pseudo code.
Condiments Mocha,Sugar,Milk;
Coffee myCoffee;
myCoffee.addCondiment(Mocha);
myCoffee.addCondiment(Sugar);
myCoffee.addCondiment(Milk);
internally inside the myCoffe object its Condiments array would now look like this
CondimentsArray:
[0][Mocha]
[1][Sugar]
[2][Milk]
Why use decoration instead of something like this?
Why use decoration instead of something like this?
The start of the Motivation section says,
"When refactoring an application to add new features, it’s best to avoid digging into code that has already been tested. This pattern provides a means of extending an object’s functionality without changing the original class’s behavior."
Your solution requires editing existing code. Decoration is used when editing the Beverage source code isn't an option.
@Daklu Aha! I see. That makes perfect sense. Thanks.
Daklu wrote:
Using patterns when they're not needed just makes the code more confusing.
I find this Decorator pattern confusing. In this example implementation, adding Whip effectively places the Beverage object inside the private data of the Whip object. Then adding Mocha places the Whip object inside the private data of the Mocha object. But what if you now need to change a property of the Beverage object? It's nested inside two owner objects and can't be easily accessed?
In the Derrick Snyder OOP Introduction (webcast here) he uses this example to demonstrate the Decorator pattern, with selection of a Beverage (Mild, House, Bold), set it's size (Tall, Grande, Venti), then add a modifier (Milk, Soy, Mocha, Whip). Once the modifiers (decorators) are added, the Beverage object is no longer accessible, so you wouldn't be able to change it's properties, such as the size, nor easily remove one of the Modifiers.
I know this is a simplified example, but there are weaknesses that make me wonder just how useful a Decorator pattern really is? And what would be a more appropriate OOP based solution to this problem?
Yes, I also came to the conclusion that there were considerable limits to the "Decorator" pattern. You can still change properties of the inner object, but only if you create corresponding methods in your decorators to sort-of "forward" the request on. So, "Change Size" method of "Condiment" calls "Change Size" on its internal "Beverage" Object. This is only feasible if the number of methods is small.
Daklu addressed this same concern about 3 comments above you guys:
Daklu wrote:
Your solution requires editing existing code. Decoration is used when editing the Beverage source code isn't an option.
In many environments, deployed code is immutable code and the only way to modify a deployed class is to decorate it.
DavidStaab wrote:
Daklu addressed this same concern about 3 comments above you guys:
Daklu wrote:
Your solution requires editing existing code. Decoration is used when editing the Beverage source code isn't an option.
In many environments, deployed code is immutable code and the only way to modify a deployed class is to decorate it.
Yes, I saw that, thanks. I understand now that this is just an example of how the decorator pattern might be applied, even though it might not be the best solution to this specific requirement. But in the Introduction to OOP webcast the Decorator pattern is specifically selected to demonstrate how to augment an existing set of classes. It seems this is the wrong approach, which begs the question: What is the correct approach for scaling an existing editable OOP design? Taking this 'Starbuzz' example where you do have the ability to edit what you like, how would one go about augmenting it to permit the addition of optional condiments? I see the arrayed approach mentioned by TheMadScientist above, but it seems a little bit non-OOPy to me. Are there other OOP-based approaches to achieving this?
The arrayed approach is how you would implement it if it was an editable class. The method in object oriented terminology is "composition".Coffee and condiments have a "has-a" relationship so you would put a condiment into Coffee. The decorator pattern pretends that a condiment is a beverage for the sake of code preservation but is not a realist representation of the flow of data between the two entities.
DavidStaab wrote:
Daklu addressed this same concern about 3 comments above you guys:
Not really. The limits of Decorator aren't specifically discussed. One needs to understand the limits of Decorator if one is going to use it.
That question might be more effective if given boundary conditions (to make it about a very specific scenario) and reposted in a new discussion thread. The point of this document was just to demonstrate the implementation of the Decorator pattern, and I simply ripped an example out of a beginner's textbook to do so.
They're discussed ad nauseum in the GOF text. And, IMHO, I think there's a need to qualify identified "limitations" in the context of the pattern's use case and purpose. The Decorator pattern is presented in both the texts I read as a solution to modifying the behaviors of untouchable classes. The "limitation" of limit the mutability of the contained class is preempted by that purpose entirely.
Thoric wrote:
What is the correct approach for scaling an existing editable OOP design? Taking this 'Starbuzz' example where you do have the ability to edit what you like, how would one go about augmenting it to permit the addition of optional condiments?
There is no single correct approach for adding condiments to the Starbuzz example. You can skin a cat many different ways, and at the end of the day they all give you the same thing--a tasty lunch. 😉
Personally my first stab would be to consider something along the lines of Mad Scientist's solution. What about it seems non-OOPy to you?
FYI, I posted an implementation of a full StarBuzz coffee shop using Actor Framework over in the AF forum. It contains this example.
In example, why cost is not a mumber of beverage? And, use beverage class as subclass of condiment, rathen than the subclass of soy, milk, mocha, can more reflect the idea of decoration.
@cxl6288: Cost is actually a member of beverage, it is overridden in the condiment decorators to add cost to the beverage they decorate.
In textual language it is:
class Beverage:
abstract cost()
class Coffee: Beverage
cost(): return 10
class Whip: Beverage
Whip(beverage):
cost(): return beverage.cost()+2
What is the reason that you don't have "Set Cost" function in any of your classes?
Any why doesn't the condiment class have a set description function?
LVCoder:
"Set Cost.vi" is not included because cost is computed from the decorations that are applied. There is no user-facing API to set the cost. The user decorates the drink.
Description is actually what I would call a bug. Description, like cost, is computed from the decorations, except for the base description that is settable by the user API. Condiment DOES have a "Set Description.vi", the one that it inherited from Beverage. That's a problem because you have actually replicated state. This is a violation of one of the big rules -- Never Inherit From A Concrete Base Class. There are plenty of times to break it, but you break it sparingly, in private, in shame, when no one is looking. 🙂
The "well, crud, we already shipped version 1.0, now how do we fix this?" solution is to override "Set Description" in Condiment to set the description on its contained beverage.
The right way to fix it is to introduce a new class "Abstract Beverage.lvclass" that has NO description field in its private data but still has both Set Description and Get Description dynamic dispatch Vis. Condiment and Beverage would still inherit from Abstract Beverage. That would avoid replicating the useless string member variable at every level of decoration.
Good questions.
AristosQueue:
If we are going to remove the description field from "Abstract Beverage.lvclass" private data then I don't understand why do we need a Set Description function at all. Just like cost is a non settable item and is calculated by the condiments, why can't we apply the same logic to description and only have a Description.vi for each class?
The ability to set the description is not the same as the ability to actually store that description.
ALL decorators have the ability to Set Description. Some (Beverage) actually have a storage field for it. Others (Condiments) just forward it to their contained Abstract Beverage.
Consider the current set up. Suppose I decorate a Espresso with Whip. The outer class is a Whip object. What is that object's private data in memory? Well, it looks like this:
Notice that there are TWO string fields. If we decorate that drink with Milk then it gets worse:
We're using bytes that we don't need to be using AND we risk confusion in the code base about which string field is the Description for this drink. We really want there to only be one string field in the entire meta object.
Thanks for the clarification, I understand it now.
LVCoder: I hadn't really looked at the details of the example before; I'd just kind of glanced through it to make sure the basics of the pattern was done well. But some of the edge details were off. I've just now posted a revised version that I think may clarify things. I left the old version for comparison.