LabVIEW Development Best Practices Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

Decorator (Wrapper) Pattern

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!

Intent

Extend a concrete object’s responsibilities dynamically. Extend the features of an existing class without refactoring tested code.

Motivation

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.

Implementation

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:

basic_app.jpg

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.):

extended_app.jpg

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.

Editorial Comments

[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.

David Staab, CLA
Staff Systems Engineer
National Instruments
Download All
Comments
PhillipBrooks
Active Participant
Active Participant
on

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)


Now is the right time to use %^<%Y-%m-%dT%H:%M:%S%3uZ>T
If you don't hate time zones, you're not a real programmer.

"You are what you don't automate"
Inplaceness is synonymous with insidiousness

TheMadScientist
Member
Member
on

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?

AristosQueue (NI)
NI Employee (retired)
on

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.

TheMadScientist
Member
Member
on

@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?

Daklu
Active Participant
Active Participant
on

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.

TheMadScientist
Member
Member
on

@Daklu Aha! I see. That makes perfect sense. Thanks.

Daklu
Active Participant
Active Participant
on
I didn't specifically say it, but neither solution is inherently better than the other.  They are different solutions for different situations.  When learning about design patterns developers frequently look for places where they can be used without understanding if they should be used.  (I know I did, and I've noticed comments from others implying the same thing.)  Using patterns when they're not needed just makes the code more confusing.
Thoric
Trusted Enthusiast Trusted Enthusiast
Trusted Enthusiast
on

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?

Thoric (CLA, CLED, CTD and LabVIEW Champion)


drjdpowell
Trusted Enthusiast Trusted Enthusiast
Trusted Enthusiast
on

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.

Active Participant
Active Participant
on

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.

Thoric
Trusted Enthusiast Trusted Enthusiast
Trusted Enthusiast
on

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?

Thoric (CLA, CLED, CTD and LabVIEW Champion)


TheMadScientist
Member
Member
on

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.

drjdpowell
Trusted Enthusiast Trusted Enthusiast
Trusted Enthusiast
on

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.

Active Participant
Active Participant
on

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.

Active Participant
Active Participant
on

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.

Daklu
Active Participant
Active Participant
on

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?

MattP
NI Employee (retired)
on

FYI, I posted an implementation of a full StarBuzz coffee shop using Actor Framework over in the AF forum.  It contains this example.

Cheers,

Matt Pollock
National Instruments
cxl6288
Member
Member
on

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.

CharlesB64
Member
Member
on

@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
LVCoder
Active Participant
Active Participant
on

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?

AristosQueue (NI)
NI Employee (retired)
on

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.

LVCoder
Active Participant
Active Participant
on

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?

AristosQueue (NI)
NI Employee (retired)
on

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:

        Untitled.png

Notice that there are TWO string fields. If we decorate that drink with Milk then it gets worse:

Untitled2.png

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.

LVCoder
Active Participant
Active Participant
on

Thanks for the clarification, I understand it now.

AristosQueue (NI)
NI Employee (retired)
on

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.

Contributors