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

Applying a Simple Decorator Pattern in Flash: Paper Doll

You can move from a conceptual example to a concrete one with a computerized paper doll game. The paper doll component is the doll being dressed, and the decorations are clothing items selected for the doll. Using ActionScript's new Loader class, different GIF files are used to decorate a base GIF image of a Victorian paper doll. As in the previous abstract example, this example uses a single concrete component and several decorations.

This concrete example does not have an interface—keeping the focus on the application's use of a Decorator pattern. However, it does have a graphic output so you can see the pattern's ultimate output. (Later in this chapter, there's an example with a full interface.)

Setting Up the Component Class

The first class is the component class, Model. From this, all other classes are subclasses. It's very simple but key to the success of the application. As you can see, it's very close to the previous abstract Decorator. It consists of a method, getDressed(), and a string variable, whatToWear, as shown in Example 4-7. Save the file as Model.as.

Example 4-7. Model.as

package
{
    //Abstract class
    public class Model
    {
       protected var whatToWear:String;
        public function getDressed():String
        {
            return whatToWear;
        }
    }
}

Keep in mind that Example 4-7 should be treated as an abstract class. So the method getDressed() needs to be created as an override public function for it to work the way we want. However, the property whatToWear doesn't need to be changed from its inherited characteristics.

Decorator Class Dressing the Dolls

Example 4-8 is the abstract Decorator class, Dresser, which extends the component class, Model. The major contribution here is simply re-implementing the getDressed() method with a reference to the inherited whatToWear property.

Example 4-8. Dresser.as

package
{
    //Abstract class
    public class Dresser extends Model
    {
        override public function getDressed():String
        {
            return whatToWear;
        }
    }
}

All of the concrete decorators will be subclassed for this class.

The Concrete Classes

Once the two main abstract classes in the Decorator pattern have been established, you're all set to create the concrete ones. In the previous minimalist Decorator example, you saw the output using the trace() statement. Instead of trace() statements, both the concrete component and decorators need to be formatted for later parsing, so a tilde character (~) has been added as a demarcation point. Because all the strings from both the concrete component and decorators are grouped together into a single large string, the tilde serves as cutting point.

Concrete component class

The concrete component class is the only concrete class that extends directly from the abstract concrete class. All of the others in this application extend from the abstract decorator class. Using a simple constructor function, Sue(), the class assigns a value to the whatToWear variable. This is enough to identify the class as an instance of the main abstract component class, Model, and to establish a unique name. All decorations use the concrete component as the target for the decorations. Save Example 4-9 as Sue.as.

Example 4-9. Sue.as

package
{
    public class Sue extends Model
    {
        public function Sue()
        {
            whatToWear="~sue";
        }
    }
}

If you want to create more concrete component classes, all you need to do is create a similar class with a different name and value for the whatToWear variable. With this structure, you have no limit to the number of new concrete components you can add.

Concrete decorator classes

Moving from the minimalist example previously shown in this chapter, it's a little easier to see how the Decorator pattern works by actually seeing something happening in a graphic display. When the initial instance of the concrete component is created, all references in the concrete decorator class are to that instance. (See Example 4-16, where the concrete component is wrapped in the decorators.) In all the concrete decorator classes, the reference to the model variable is a reference to the concrete component object. In this case, that's the instance of the Sue() class, but it can be any instance of any concrete component. That's why, if you wish to expand the application to include more concrete components (paper dolls to dress), you don't have to make any fundamental changes. Just add another concrete component class. In Example 4-10 to Example 4-15, the captions are the filenames.

Example 4-10. OrangeDress.as

package
{
    public class OrangeDress extends Dresser
    {
        private var model:Model;
        public function OrangeDress(model:Model)
        {
            this.model=model;
        }
        override public function getDressed():String
        {
            return model.getDressed() + "~orangedress";
        }
    }
}

Example 4-11. BlueDress.as

package
{
    public class BlueDress extends Dresser
    {
        private var model:Model;
        public function BlueDress(model:Model)
        {
            this.model=model;
        }
        override public function getDressed():String
        {
            return model.getDressed() + "~bluedress";
        }
    }
}

Example 4-12. Bow.as

package
{
    public class Bow extends Dresser
    {
        private var model:Model;
        public function Bow(model:Model)
        {
            this.model=model;
        }
        override public function getDressed():String
        {
            return model.getDressed() + "~bow";
        }
    }
}

Example 4-13. Umbrella.as

package
{
    public class Umbrella extends Dresser
    {
        private var model:Model;
        public function Umbrella (model:Model)
        {
            this.model=model;
        }
        override public function getDressed():String
        {
            return model.getDressed() + "~ umbrella ";
        }
    }
}

Example 4-14. Hat.as

package
{
    public class Hat extends Dresser
    {
        private var model:Model;
        public function Hat(model:Model)
        {
            this.model=model;
        }
        override public function getDressed():String
        {
            return model.getDressed() + "~hat";
        }
    }
}

Example 4-15. Muff.as

package
{
    public class Muff extends Dresser
    {
        private var model:Model;
        public function Muff(model:Model)
        {
            this.model=model;
        }
        override public function getDressed():String
        {
            return model.getDressed() + "~muff";
        }
    }
}

All six decorations can be used in any combination you want with the instance of the Model class. This next section shows how to implement the Decorator pattern using the different decorators.

Implementing the Paper Doll Decorator

As noted, no interface was created for implementing this application, and so you can see clearly how the concrete component is wrapped by the decorators. Then, on the stage, you can see the effects of the different combinations.

The sequence is:

  1. Instantiate a concrete component.

  2. Wrap the concrete component in the desired decorator instance using the format:

    componentInstance = new DecoratorInstance(componentInstance)

  3. Apply the getter method (getDressed()) to the concrete component instance initiate to get the fully wrapped values. The values will include those of the concrete component instance and the applied decorations.

Example 4-16 illustrates how to implement the paper doll decorator.

Example 4-16. FashionShow.as

package
{
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.display.Sprite;
    public class FashionShow extends Sprite
    {
        var ensemble:Array=new Array();
        public function FashionShow()
        {
            trace("||--Working--||");
            var doll:Model = new Sue();
            doll=new Hat(doll);
            doll=new OrangeDress(doll);
            //doll=new BlueDress(doll);
            //doll=new Umbrella(doll);
            doll=new Bow(doll);
            doll=new Muff(doll);
            var ready2wear:String=doll.getDressed();
            sewingMachine(ready2wear);
            for (x=0; x < ensemble.length; x++)
            {
                clothesOn(ensemble[x]);
            }
        }
        private function sewingMachine(wardrobe:String):Array
        {
            ensemble=wardrobe.split("~");
            ensemble.shift();
            return ensemble;
        }
        private function clothesOn(outfit:String)
        {
            var clothier:Loader=new Loader();
            var item:String="clothes/" + outfit + ".gif";
            var getItem:URLRequest=new URLRequest(item);
            clothier.load(getItem);
            this.addChild(clothier);
        }
    }
}

Once you've got everything ready to go, you'll need some GIF files. The model (concrete component) should be an image of a doll that you will clothe with the Decorator pattern. Each filename needs to be the name of the value assigned to the whatToWear variable in the concrete component or decorator listing, minus the tilde (~) character. For example, the concrete component, (Sue), whatToWear variable is assigned the string value "~sue" and so the GIF filename for that component would be sue.gif. The decorator BlueDress has a whatToWear value of "~bluedress," and so the image with the blue dress would be named bluedress.gif. Place all the GIF files, including the file representing the doll model, in a folder named clothes.

Finally, open up a new Flash document and set the stage dimensions to 300 × 650. In the Properties panel's Document class window, type in FashionShow. The size and shape of your stage will depend on the size of the paper dolls you use.

Figure 4-6 shows two different combinations of decorations on the paper doll. The image on the left decorates with the hat, bow, orange dress, and muff, while the second is the blue dress, the bow and an umbrella. Each appears very different just by changing the combination of decorations.

Paper doll with two different decoration sets

Figure 4-6. Paper doll with two different decoration sets

Instead of paper dolls, any component could be decorated with any characteristics. Also, you can include more than a single property or method in a decoration. In the next section, we'll take a look at adding additional properties with the Decorator design pattern.

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