Understanding the Decorator Pattern


The basic idea of the Decorator pattern is that new types of objects (decorators) can add new behavior or change existing behavior of a decorated object. The decorators and the decorated object must implement the same interface. That way, the decorator and decorated objects can be used interchangeably.

The Decorator pattern consists of the following elements:

  • Decorator/decorated interface

  • Concrete decorated class

  • Abstract decorator class

  • Concrete decorator class(s)

The following sections look at these elements in more detail.

Decorator/Decorated Interface

Everything about the Decorator pattern hinges on the decorator objects and the decorated objects implementing the same interface. This way, they can be treated in exactly the same way and used interchangeably. The exact interface depends entirely on the required behavior of the objects. There is no universal interface for all decorator and decorated objects. For the purpose of a simple example, consider the following interface. In the next few sections, we'll show how to implement this interface using the Decorator pattern.

package com.peachpit.aas3wdp.decorator {    public interface IWidget {       function getDescription():String;       function run():void;    } }


Tip

The distinction between decorator and decorated objects might not be immediately clear. Decorators are objects that use composition to add to or modify the behavior of another object at runtime. The object to which the behavior is added or modified is the decorated object. Because decorator objects themselves can be decorated by other decorators, it's essential that both types of objects implement the same interface.


Concrete Decorated Class

The concrete decorated class is the basic type that implements the interface. Continuing the example from the preceding section, the following Widget class implements IWidget as the basic decorated type:

package com.peachpit.aas3wdp.decorator {    public class Widget implements IWidget {       public function Widget() {}       public function getDescription():String {          return "Widget";       }       public function run():void {          trace("running");       }    } }


This simple decorated type is the basis of the pattern (like the innermost Russian doll). The base decorated type is the foundation on which all other decorators are applied. This example is purposefully simple. It merely implements the two methods required by the interface (IWidget).

Abstract Decorator Class

Technically, all that is required of a decorator is that it implements the same interface as the decorated type. However, practically speaking, most decorators inherit from an abstract decorator class that implements some of the basic functionality such as compositing the decorated object and passing through the method calls. The following example illustrates a basic abstract decorator class for the widget example:

package com.peachpit.aas3wdp.decorator {    public class AbstractWidgetDecorator implements IWidget {                 protected var decorated:IWidget;          public function AbstractWidgetDecorator(decoratedWidget:IWidget) {          decorated = decoratedWidget;       }                    public function getDescription():String {          return _decorated.getDescription();       }       public function run():void {          _decorated.run();       }    } }


The AbstractWidgetDecorator class must implement the IWidget interface because it is the same interface implemented by the decorated type (Widget). This is the basis of the Decorator patternthat both the decorator and the decorated types implement the same interface.

Note that the constructor accepts a parameter of type IWidget. This parameter is the object that the decorator will decorate. Although there's no requirement that you pass the decorated object through the constructor (you could use a different method to accomplish this), it is the convention. It's important that the decorated type is set to the interface rather than a concrete (or abstract) type in order to fully support polymorphism. This approach allows the decorator to decorate not only a concrete decorated type, but also other decorators.

It's also important to note that in this example the decorated property is set as protected. By setting the property as protected, it is accessible to subclasses of AbstractWidgetDecorator.

The actual implementation of the methods may vary in every case. In this particular example, each of the methods simply passes through the request to the decorated object. However, it is useful to have an abstract class in many cases to ensure that the core, common behavior is inheritable.

Concrete Decorator Class(es)

The concrete decorator(s) must implement the same interface as the decorated object type. Normally, this is accomplished by extending the abstract decorator class. The decorator class can do the following.

  • Modify existing behavior. Decorators often proxy requests to decorated methods. Although it's possible for a decorator to simply pass through requests to the decorated object exactly as they were made, a decorator can also pre-process or post-process. Decorators can also handle the entire request at the decorator level without ever forwarding the request to the decorated object.

  • Add new behavior. Decorators must implement the interface, but they can also add new methods. This is an option and not a requirement. Adding new behavior can be advantageous because you can add functionality to an object at runtime. However, adding methods means that you cannot effectively chain decorators as is discussed later in this chapter.

The following code illustrates a concrete decorator for the widget example:

package com.peachpit.aas3wdp.decorator {    public class DigitalWidget extends AbstractWidgetDecorator {       public function DigitalWidget(decorated:IWidget) {          super(decorated);       }                 override public function getDescription():String {          var description:String = _decorated.getDescription();          return "digital " + description;       }    } }


This example declares DigitalWidget to extend AbtractWidgetDecorator. Note that the constructor accepts a parameter of type IWidget, meaning that it could be either a concrete decorated type (Widget) or another decorator type. The constructor in this example simply passes along the parameter to the constructor of the superclass.

This example inherits the default implementation for the run() method, but it overrides getdescription(). The getdescription() method returns the description from the decorated object prepended with the word digital.




Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net