Wiki Home

Decorator Pattern

(Updated: 2005.04.05 10:55:31 PM)
Namespace: SoftwareEng

Decorator Pattern is one of the 23 Design Patterns elucidated in the most excellent Gamma And Helm book, Design Patterns: Elements of Reusable Object - Oriented Software by Gamma and Helm et al. ISBN 0201633612 , which is cited below:
Intent: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. (Also known as Wrapper)

Design Pattern Classification: Object Structural

Applicability:
The Decorator Pattern is useful when:
  • to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects.
  • for responsibilities that can be withdrawn.
  • when extension by subclassing is impractical or impossible.
    Structure:

  • Component - defines the interface for objects that can have responsibilities added to them dynamically.
  • ConcreteComponent - defines an object to which additional responsibilities can be attached.
  • Decorator - maintains a reference to a Component object and defines an interface that conforms to Component's interface.
  • ConcreteDecorator - adds responsibilities to the component.
    Collaborations:
    Note - There are several other important implementation issues to consider before dishing out decorator objects, be sure and see the Gamma And Helm book or CD ( ISBN 0201633612 ) if you're new to Design Patterns.
    Decorator and VFP (By Steven Black)

    A decorator wrapper (I'll just call it Decorator) is an elegant OOP technique that can be handy in a wide variety of situations. In particular, the Decorator pattern is useful for extending the functionality of a class without polluting (or even needing) the original source.

    Subclassing isn't always cool

    But before we get to the decorator pattern, lets examine what happens when we allow a class to grow organically with inheritance. To illustrate, consider the following class interface for a frog:
    DEFINE CLASS Frog AS CUSTOM
      FUNCTION Jump(nValue)
      FUNCTION Eat()
    ENDDEFINE

    Amphibians are easily created with Visual FoxPro. Suppose that we needed to create a new sort of frog, one that can dance. We could subclass Frog as follows:
    DEFINE CLASS DancingFrog AS Frog
      FUNCTION Dance()
    ENDDEFINE

    Suppose we later need a singing Frog. Well, within our current hierarchy, we have several obvious options.
    1. We could simply augment the DancingFrog with a Sing() method, perhaps renaming the class, if that's possible, to EntertainingFrog to better reflect its enhanced functionality.
    2. We could subclass the DancingFrog, add a Sing() method to the new subclass, and call the new class, say, DancingSingingFrog.
    3. We could insert a new Frog subclass in the hierarchy, before DancingFrog, endow it with a Sing() method, and call it, say, SingingDancingFrog.
    4. We could make all Frogs able to sing by augmenting original Frog class.
    5. What the heck, we could vastly simplify this rapidly exploding hierarchy by eliminating the DancingFrog class and give every Frog the Sing() and Dance() methods in a single and monolithic Frog class.

    So, buffeted by the needs of this current implementation, you make your choice and you take your chances. What if you later needed a burping frog, one that belches after meals. Do we subclass again? If so, where? Do we add a Burp() method, or do we simply augment the Eat() method because problem analysis indicates that burping naturally follows from eating. Again, which hierarchical level is best for modification? And later, when you need a frog that can beg, roll-over, shake-a-leg, and stay, a new question eventually arises: "How did this Frog class become such a mess?".

    At the outset, a pragmatic reuse artists would be correct in asking when, if ever, will the need for a singing, dancing, and belching frog ever arise again? Why not just augment the frog for this instance, without polluting with trivial nuance our general, simple, and reusable Frog class?

    The Decorator alternative

    The answer may be to use a decorator. A decorator is, in essence, a class with mostly "pass-through" behavior. It "wraps" a class by reference, forwarding all messages to the reference except for the messages the wrapper is designed to intercept. Setting up a decorator takes a bit of work, ( Take a look at Decorating With THISAccess for a really simple yet comprehensive way of implementing a decorator in VFP6+ without the "bit of work" ), but thereafter it's a snap to use.

    Consider the following DecoFrog class:
    DEFINE CLASS DecoFrog AS CUSTOM
      oRealFrog= .NULL.
    
      FUNCTION INIT( oFrog)
      THIS.oRealFrog= oFrog
    
      FUNCTION Jump(n)
      THIS.oRealFrog.Jump(n)
    
      FUNCTION Eat()
      THIS.oRealFrog.Eat()
    ENDDEFINE

    Sample 1. Notice the key element of decorators: They are "transparent", passing through all messages except for those that need to be intercepted. Creating an abstract decorator class (like the one above) that passes through everything is the first step. Now to augment the frog for a particular instance, we can use a simple subclass of the decorator without polluting the Frog class.

    To the outside world, the DecoFrog class has the same programming interface as a Frog. But it's not a Frog, it's a lens through which we can "see" a Frog. If we need a specialized one-off Frog, like one that sings, we could do this:

    DEFINE CLASS DecoSingingFrog AS FrogDecorator
      FUNCTION Sing()
      ? WAIT WINDOW "It's not easy being green..."
    ENDDEFINE


    Similarly, we can define a dancing frog by simply subclassing the class DecoFrog as follows:
    DEFINE CLASS DecoDancingFrog AS DecoFrog
      FUNCTION Dance()
      DecoFrog::Jump(+1)
      DecoFrog::Jump(-2)
      DecoFrog::Jump(+1)
    ENDDEFINE

    Retrofitting a decorator is easy. Where before your code looked like this:
    Kermit=CREATE( "Frog")

    A singing frog can now be substituted at run-time like this:
    Kermit=CREATE("Frog")
    Kermit=CREATE("DecoSingingFrog",Kermit) 

    or more succinctly:
    Kermit=CREATE("DecoSingingFrog",CREATE("Frog"))

    (Note that nesting CREATEOBJECT statements work just fine in Visual FoxPro).

    Additionally, decorators can be chained, with functionality added or modified as needed! This gives you considerable pay-as-you-go flexibility. For example, to build a singing and dancing frog, do as follows:

    Kermit=CREATE("Frog")
    Kermit=CREATE("DecoSingingFrog",Kermit)
    Kermit=CREATE("DecoDancingFrog",Kermit)

    or, if you prefer one-line of code:
    Kermit=CREATE("DecoDancingFrog", CREATE("DecoSingingFrog",CREATE("Frog")))

    Which creates an object relationship illustrated by the following object diagram:

    Figure 1. Two decorators chained together, both augmenting the object. Well, Tada! We now have an ordinary Frog named Kermit that appears, in this instance, with the ability to sing and dance, and we didn't need to pollute the Frog class to get it. In fact, we didn't need the source to class Frog.

    Decorator Benefits

    Here are some benefits that come from using decorators. Decorator Downsides
    As with all things, the "no free lunch" pattern applies: You cannot get the added flexibility afforded by using a decorator without some tradeoffs. Here are some of them:

    Figure 2. One of the difficulties of managing a decorator is maintaining its interface in synch with the objects to be decorated. Problems can be greatly alleviated by making the decorator a subclass of an abstract class, one whose sole purpose is (by definition) to define the class interface.


    Figure 3. Here is how the Frog class might look, with emphasis on the evolution of its decorators to enhance otherwise normal and unentertaining frogs.

    Conclusion
    Decorators change the appearance of an object in a way that is fundamentally different from subclassing. Subclassing changes class internals. A decorator changes the class appearance.

    Decorators are not solutions for every situation, but occasionally they are just the ticket. The next time you find yourself sub-classing for the purposes of a particular implementation, ask yourself if a decorator wouldn't better suit your situation.
    See also: VFP Design Pattern Catalog Decorating With This Access
    Category Design Patterns