O'Reilly logo

ActionScript 3.0 Design Patterns by Chandima Cumaranatunge, William Sanders

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Minimalist Abstract Decorator

To get started with the Decorator design pattern, a minimum implementation needs the following:

  • An abstract component

  • An abstract decorator

  • A concrete component

  • Concrete decorators

For the sake of clarity, two decorators will be devised so that you can better see how the structure works (and it's closer to the model shown in Figure 4-1.) Of all the classes devised in this pattern, by far the most important is the initial abstract class modeling the component. Every other class is subclassed from this initial class.

Abstract Component Class

To begin the process of creating a Decorator design pattern, create a component. You might think of a component as an undecorated Christmas tree, or even a person deciding what to wear. It's simple because all of the added features are handled by decorators. Example 4-1 has only a single string variable that will be used to describe the component. Save the script as Component.as.

Example 4-1. Abstract Component class

package
{
    //Abstract Component in Decorator Design Pattern
    //**************
    //Abstract class
    public class Component
    {
        internal var information:String;

        public function getInformation():String
        {
            return information;
        }
    }
}

From this point on, all the classes will extend the Component class. Keeping in mind that the class is an abstract one—or at least is treated as one—its primary function is to establish a basic structure for the rest of the application. It contains a single variable, information, and a single getter function, getInformation(). These elements set up both the concrete components and decorations. Both components and decorations need to display information about their characteristics. A concrete Christmas tree displays information that lets you know that it's a Christmas tree instead of another kind of object that can be decorated, such as a front yard to be decorated with gnomes and pink plastic flamingoes. Additionally, you want to be able to retrieve information, and so it has a getter function.

Abstract Decorator Class

Next, the abstract decorator is a subclass class of the Component class that inherits the information variable, so nothing is needed as far as the information variable is concerned. In fact, nothing's required for this simple example other than defining the class as an extension of the Component class. However, the getInformation method is re-implemented independently of the Component class, using the override statement—which does what it says on the tin; it overrides parent class methods. This is done to distinguish the same method being used for the Decorator class from the method being used for the Component class. All the concrete decorations are subclassed from the Decorator class, and all concrete components are subclassed directly from the Component class. Further on, the concrete components will be wrapped in concrete decorators, and such distinctions become important in more complex implementations of the Decorator design pattern. The trace() statement is used to show you where in the process the abstract Decorator class appears. Save Example 4-2 as Decorator.as.

Example 4-2. Decorator.as

package
{
    //Abstract Decorator in Decorator Design Pattern
    //**************
    //Abstract class
    public class Decorator extends Component
    {
        trace("|*|Decorator|*|");
        override public function getInformation():String
        {
            return information;
        }
    }
}

Once the two abstract classes, Component and Decorator, have been established, it's time to work with the concrete classes. For this example, only a single concrete component is created. Cleverly named ConcreteComponent, this class represents whatever will be decorated in a Decorator design pattern. You can have multiple concrete components that all use the same set of decorations, or only a single one. Later in this chapter, you will see an application where multiple concrete classes are decorated by a single set of decorators. The nice thing about the Decorator is that you can add as many concrete components as you want. Imagine a business web site where the concrete component represents an e-business site that you've worked on for several months. Using a Decorator design pattern, you've developed several useful and tested elements that are applied using decorators. Shortly after the project is complete, you get a request to develop another site with a different main product and an overlapping set of elements. Rather than starting from scratch, all you have to do is to add a different concrete component class and some new decorators that aren't available in the original set.

Example 4-3 shows that the concrete component does little more than extend the Component class and add a constructor. It inherits all of the features of the Component class, and uses the information variable to place a message in the Output window to point to the decorators. Save the code in Example 4-3 as ConcreteComponent.as.

Example 4-3. ConcreteComponent.las

package
{
    //Concrete Component
    public class ConcreteComponent extends Component
    {
        public function ConcreteComponent()
        {
            //\u2794 is Unicode for a right-pointing arrow
            information = "Concrete Component is decorated with \u2794";
        }
    }
}

Note

In Example 4-3, you see that Unicode is inserted using the format \u + character code value. In the example, an arrow character is formed using Unicode 2794. To search for a character you may want to use, see http://www.fileformat.info/info/unicode/char/search.htm. You'll find several different arrow characters you can use, including \u0363, \u2192, \u21aa, and \u21d2.

The next step is to build concrete decorator classes. In this abstract example, two concrete decorators will wrap themselves around the concrete component. So, to get started, we'll need a Component instance variable to hold the component we're wrapping.

var components:Component;

The variable is named components, with an "s," because component is a built-in word in ActionScript 3.0. This variable is referenced in the decorator's methods. Next, we need a way to affix the components variable to the object being wrapped. The following code shows how the component being wrapped is passed to the decorator's constructor.

public function DecConA(components:Component)
{
    this.components=components;
}

Finally, you need a getter function to get the unique information from the concrete decorator. The public method inherited from the Decorator class must be set up using the override statement. Here, the method gets both the concrete component's information [components.getInformation()], and adds on that of the concrete decoration [+ " Decoration Alpha:"]:

override public function getInformation():String
{
    return components.getInformation() + " Decoration Alpha:";
}

Concrete Decorations

Now, we're all set to write the concrete decorator classes. Save Example 4-4 and Example 4-5 as DecConA.as and DecConB.as, respectively.

Note

In several of the decorator classes, you'll see:

this.components = components;

Such code is a non-standard use of the this identifier, but it makes sense in this context because it differentiates the variable and parameter. Normally, you wouldn't use the this identifier in this manner. The variable and parameter names are identical to emphasize the use of the abstract class in both cases, with one passing the value to the other.

Example 4-4. DecConA.as

package
{
    //Concrete Decorator "Alpha"
    public class DecConA extends Decorator
    {
        private var components:Component;
        public function DecConA(components:Component)
        {
            this.components=components;
        }
        override public function getInformation():String
        {
            return components.getInformation() + " Decoration Alpha:";
        }
    }
}

Example 4-5. DecConB.as

package
{
    //Concrete Decorator "Beta"
    public class DecConB extends Decorator
    {
        var components:Component;
        public function DecConB(components:Component) {
            this.components=components;
        }
        override public function getInformation():String
        {
            return components.getInformation() + " Decoration Beta:";
        }
    }
}

Wrapping Up

To execute the Decorator design pattern, the whole key lies in knowing how to wrap a component in a concrete decorator. First, you need to instantiate a concrete component.

var testComponent:Component = new ConcreteComponent();

Then, you wrap the component in one or more decorations using the following format:

componentInstance=new ConcreteDecorator(testComponent);

So, in our example, with two concrete decorations, we'd write:

testComponent=new DecConA(testComponent);
testComponent=new DecConB(testComponent);

At this point, testComponent is decorated with two decorations. We could duplicate the above lines adding the same two decorations as often as we wanted. Think of the decorations as red and green Christmas tree ornaments. The tree could be covered with nothing but red and green ornaments, rather than just one of each. The Decorator design pattern is employed cumulatively. That is, as you add each decoration, it's added to those already wrapping the concrete component.

Finally, to see what havoc we're wrought, we use the getter method—getInformation():

trace(testComponent.getInformation());

To see how all of this works, save Example 4-6 as DecTest.as in an ActionScript file. Then open a new Flash document file, and type DecTest in the Document class window in the Properties panel.

Example 4-6. DecTest.as

package
{
    import flash.display.Sprite;
    public class DecTest extends Sprite
    {
        public function DecTest()
        {
            //Instantiate Concrete Component
            var testComponent:Component = new ConcreteComponent();
            //Wrap first decorator around component
            testComponent=new DecConA(testComponent);
            //Wrap second decorator around component
            testComponent=new DecConB(testComponent);
            //Output results
            trace(testComponent.getInformation());
        }
    }
}

Figure 4-5 shows what you should see in your Output window when you test the movie.

Decorations on component

Figure 4-5. Decorations on component

To understand what's going on in the Decorator pattern, go back and look at Figure 4-2. The example application first instantiated a ConcreteComponent() object. That object displays a message pointing to its decorations. Imagine that object (testComponent) as the smallest can to the far left in Figure 4-2. That can is then placed into decorator Can #1. At this point, the concrete component object (testComponent) is decorated with Can #1, but retains its original properties –much in the same way that a lawn decorated with a family of gnomes still retains its property of green grass. Next, Can #1, which now contains the concrete component, is dropped into Can #2. Now Can #2 has both Can #1 and the Concrete component Can. Thus, Can #2 has all of the properties of itself plus those of the cans inside.

In a sense, the whole process works like the compound operator, plus-equal (+=). Each decorator attaches itself to the existing component and its decorator. As each decorator is added, all the previous ones are retained but not duplicated, unless you add the same decorator more than once. So, as the output shows, you can add as many decorations as you want simply by wrapping them in the existing object in one of the decorators.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required