An Enigma Wrapped in a Clear Casing
The Decorator Design Pattern is an enigma wrapped in a perfectly clear casing. I can think of fewer design patterns that are clearer in terms of what they do or seem more practical. By using concrete decorations, you can change an object without touching its structure. You can pick and choose what features you want to add, and your central object (or Concrete Component) is untouched. You want to add another feature? ¡No problema! Just add another decoration. Likewise, if you want to remove an unwanted feature, just remove the decoration you no longer want. All of this changing is accomplished without touching the object all the while the object is effectively changed by the decorations. So updating is simply a matter of changing decorations and/or adding new components to be decorated. (Download all files for program here )
In our book, the most real world practical example was a car dealership where different models of hybrid cars (Components) were set up with different accessories (Decorators) to arrive at a final price. As different models of hybrids became available, they could be added to the components and as different accessories came and went, they could be added to the program without having to disrupt the structure of the program. The pricing of the car was based on both on the base price (Component) and the whistles and bells (Decorators) the buyer selected. Once set up, the site is easy to maintain, update and reuse in a different context.
In this post we want to provide a simple yet usable example that can be modified for your own needs. Many of the simple examples we’ve seen (or created ourselves) have made extensive use of string and numeric variables and/or trace output. Given the fact that practical applications make extensive use of such variables, there’s nothing wrong with that. However, we’ve found that the most difficult part of creating design patterns with ActionScript 3.0 is in making use of graphic materials and movie clips.
The enigmatic part of the Decorator is in setting it up. In order to see what I mean, take a look at the class diagram in Figure 1.

Figure 1: Decorator Class Diagram
Most of the details about the way this design pattern works are in our book, but I think a discussion of some of the features of this pattern would be helpful to point out a couple of things that struck me as a bit enigmatic.
In looking at what varies, we find that the variation is responsibilities of an object without sub-classing—in other words, use delegation. Of all of the things that can vary in a program structure, those assigned to the Decorator still need further clarification. As you will see in the next section, the Decorator seems to even contradict its own element of variation because it double-subclasses. However, once everything is straightened out, you will find the Decorator example we use to be very simple to implement and even use in a real live work situation.
An Abstract Class as a Subclass
One of the most unusual elements of the Decorator pattern is the fact that the abstract class, Decorator, is a subclass of the base abstract class, Component. The concrete component classes derived directly from the Component class act in a very similar way as the concrete decorator classes. This is because they have an almost identical interface. However, the concrete decorator classes wrap a Component reference inherited from the Decorator.
In some Decorator designs, the wrapper is actually in the abstract Decorator class. They are set up with a constructor in the abstract class itself, but as you know, ActionScript 3.0 has no abstract classes. While it might be possible in the ersatz abstract classes we use in AS3, a constructor seems to be superfluous. First, all of the decorators are typed as components in our design. You will see:
private var block:Component=new ConcreteComponent();
…
block = new ConcreteDecorator1(block);
That’s possible because the abstract Decorator class extends the abstract Component class. Even though the object is instantiated using Component typing, the same interface is inherited in the concrete decorators because their parent class (Decorator) inherits the interface of the Component class.
Time Out: At this point your brain may be saying, I should have listened to my mother who told me to stay away from those design pattern hoodlums. They’re nothing but trouble, and you’ll rue the day that you had anything to do with them. Why don’t you work for your nice cousin, the drug dealer? Well maybe you’re not thinking that, but that kind of code can jolt your brain. It’s perfectly normal, and it’ll go away in 9-10 years or when you die. See? Your mother was right, just like when she told your sister not to date guys with the nickname, killer.
Second, the design suggests that the concrete decorators be instantiated by wrapping the component instance. That would mean that the constructors would have to override the parent class constructor function. I don’t suppose there’s anything wrong with that, but I saw nothing to be gained from it. In their work, the Freemans (Head First Design Patterns) similarly did not include the wrapper in the abstract Decorator, even though Java does have an abstract class modifier. In any event, the decorator wraps the component and that allows the component and decorator elements to share much of the design’s structure.
Creating and Using Shape Components and Decorators
Other than the fact that the Decorator abstract class is a subclass of the Component abstract class and the Decorator wraps the Component class, the rest of the design is pretty straightforward. To understand this pattern, I believe that we’d be best served by starting with the results and then drilling down to see how it was done. Figure 2 shows what you will see with the default settings in the Client class.

Figure 2: Three Decorators and one Component
To both illustrate what’s happening and separate the decorator and component objects, I used a simple design. The black rounded rectangle is a component instance and the three colored round “filters” are decorators. The black component uses a solid fill and the decorators use 50% alpha fills. With this design, adding additional decorators and components is simple. Just add another concrete decorator or component class. Any shape can be used as either component or decorator.
The Component and Decorator Abstract Classes
In this example, both the base and sub-base classes have been kept simple. Keep in mind that this Decorator pattern is set up to be easy to use and practical at the same time. We’ll begin with the Component class. All it does is to provide the interface for the pattern. It has a single method, getFigure(), that can be used by both the Components and Decorators. It returns a Shape object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //Component //Abstract class: Do not instantiate package { import flash.display.Shape; public class Component { public var figure:Shape=new Shape(); public function getFigure():Shape { return figure; } } } |
That’s not difficult at all. Any reference to a Shape object as a return object requires us to import the Shape class, but otherwise, it’s just a plain vanilla return function.
The base Decorator class is also very simple to build. Because it extends the Component class, it too inherits the single getFigure() method; so there’s no need to include it again. However, as you can see in the class diagram, it needs to hold a reference to the Component class. In fact, that’s about all it contains that you need to include in the code as can be seen in the following listing:
1 2 3 4 5 6 7 8 9 | //Decorator //Abstract class: Do not instantiate package { public class Decorator extends Component { protected var block:Component; } } |
Keep in mind that the Decorator is an abstract class, and like all abstract classes, you do not instantiate it. Just because it is a subclass does not mean it can be instantiated.
Some of you who have read the Freemans’ Head First Design Patterns chapter on the Decorator (the URL to which is at the end of this post) may have noticed that they did include or re-implement the main operation ( getDescription()) in the Decorator. However, they did so as an abstract method, which is available in Java but not in ActionScript 3.0. Further, they concatenated another element in their concrete decorators. Nothing is being concatenated to the Shape object in this example, and it wouldn’t be possible anyway—as a concatenation at least.
The Concrete Component and Decorator Classes
Given that this Decorator example is very simple and has only a single method, you will not see a lot differences between the concrete component and the decorators. However, that makes it all the easier to implement and better understand what’s going on. First of all, the ConcreteComponent class extends the Component class. The constructor function creates a simple Shape object. Because it is a subclass of the Component class, it has the getFigure() method and the Shape typed figure property. This may seem to be an unnecessary redundancy because the constructor should take care of creating the shape; however, it still needs the getFigure() method to return it to a request by the Client class.
1 2 3 4 5 6 7 8 9 10 11 12 13 | //ConcreteComponent package { public class ConcreteComponent extends Component { public function ConcreteComponent() { figure.graphics.beginFill(0x000000,1 ); figure.graphics.drawRoundRect(100,100,250,200,8); figure.graphics.endFill(); } } } |
You can create as many concrete component classes as you want. Here we’ve just created one to keep it simple. Further on in this post, you will be given an opportunity to add more concrete component classes in a contest involving the Decorator design pattern.
Next, we will create the three decorator classes. You will note that instead of drawing a rounded rectangle, each draws a circle and each uses 50% alpha instead of 100%. Also, the decorator constructors wrap a Component instance. The Shape is created in the getFigure() operation which overrides the same operation in the Constructor class. We could have placed the Shape construction in the constructor function like we did with the ConcreteComponent class but chose not to in order to suggest more flexibility. Besides, the constructor function really should not be used for too much serious work. (See Miško Hevery’s article on making the constructor doing real work.) The following three class are identical except for their names and the fill color of the circles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //Decorator package { import flash.display.Shape; public class ConcreteDecorator1 extends Decorator { public function ConcreteDecorator1(block:Component) { this.block = block; } public override function getFigure():Shape { figure.graphics.beginFill(0xff0000,.5 ); figure.graphics.drawCircle(225,100,100); figure.graphics.endFill(); return figure; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //Decorator package { import flash.display.Shape; public class ConcreteDecorator2 extends Decorator { public function ConcreteDecorator2(block:Component) { this.block = block; } public override function getFigure():Shape { figure.graphics.beginFill(0x00ff00,.5 ); figure.graphics.drawCircle(225,200,100); figure.graphics.endFill(); return figure; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //Decorator package { import flash.display.Shape; public class ConcreteDecorator3 extends Decorator { public function ConcreteDecorator3(block:Component) { this.block = block; } public override function getFigure():Shape { figure.graphics.beginFill(0x0000ff,.5 ); figure.graphics.drawCircle(225,300,100); figure.graphics.endFill(); return figure; } } } |
First, note the fact that literals are used for the colors, location and size. You may be thinking that doesn’t provide a lot of runtime flexibility with the shapes. Actually, it’s not that big of a deal. If you want, you can add parameters to the getFigure() operation without losing stride. However, the focus is on what the concrete decorators do and how they can be used as objects to change a concrete component; so for the time being don’t worry about adding parameters. (You can add parameters later.)
The Client
In many of the more recent examples, we’ve been adding user interfaces (UI) to the Client class for making requests from a patterned program in both Flex and Flash. In this example, we do not for two reasons. First, by using a few requests in the client not associated with a UI, you can clearly see how to make requests from the Decorator. Secondly, we’re having a contest where you will be providing your own UI in either Flash or Flex using ActionScript 3.0. (See The ActionScript 3.0 Decorator Design Pattern Contest for rules). For now, though, here’s the Client class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //Client for Decorator Design Pattern package { import flash.display.Sprite; public class Client extends Sprite { private var block:Component=new ConcreteComponent(); public function Client() { addChild(block.getFigure()); block = new ConcreteDecorator1(block); addChild(block.getFigure()); block = new ConcreteDecorator2(block); addChild(block.getFigure()); block = new ConcreteDecorator3(block); addChild(block.getFigure()); } } } |
In looking at the requests in the Client everything is centered on the block Component object. The object is first typed as a Component and then instantiated as a ConcreteComponent. The rest of the Client class does little more than make requests through the Component.getFigure() method. Each of the decorators is created by the desired concrete decorator wrapping the block object. Of course each concrete decorator is used just once and added to the DisplayObjectContainer and then destroyed as the block instance is re-instantiated with a new concrete decorator.
Into the Lunch Bucket
Now then, some of you may be thinking, that’s not so bad, and you’d be right. Sometimes the more daunting design patterns turn out to be pretty simple and sensible after all. You can add more concrete component classes by copying, pasting and renaming the ConcreteComponent class to ConcreteComponentA, etc. Change the Shape object in the new concrete component class, and Bob’s your uncle, it’s done! Likewise, you can do the same with concrete decorators. Add and change them all you want, and you’ll see that the Decorator design pattern structure handles them all with aplomb. Nothing falls apart or goes boom! It just keeps on truckin’.
You can see the Least Knowledge principle at work here. In part, you can see it in how the changes in the Component are delegated to the Decorator. Also, note that you need not drill down to access the different decorators. One dot does the trick (e.g., block.getFigure()) to bring up both the concrete component and decorators.
Finally, you can get a Free Chapter on the Decorator Design pattern from the Freeman’s Head First Design Patterns book. All of the examples are in Java, but it’s full of good stuff about using the Decorator, and all of it can be extrapolated for using with ActionScript 3.0. (Did I mention it’s free?)
So between our chapter on the Decorator in our book, this post about the Decorator, and the Freeman’s Decorator chapter, this is one design pattern you ought to be able to use in your work. To get started, why not enter our first Golden Lunch Bucket Contest? To practice, here’s what you can do (and see how freakin’ easy the pattern is to use):
- Create a new concrete decorator with an entirely different Shape and add it to the program.
- Change the existing Shape in the concrete component and add a new concrete component with yet another shape.
- Change the Client to execute the program with the new concrete decorator and new and changed concrete component
That ought to do it for this Lunch Bucket Design Pattern. Be sure to take a look at the first Golden Lunch Bucket Contest that uses the design pattern in this post!

The ActionScript 3.0 Easy and Practical Decorator Design Pattern by William B. Sanders, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.
Related posts:

Bill Sanders
Hello again :)
I am quite new to this amazing world of Design Patterns from a learning view, I was just wondering if you were using say, ‘Simple Factory’ if you were to add a decorate function, eg. decorate(obj:Shape, type:String) could you add this to the factory? Or do you think it should stay within the client.
Thanks for this article!
Josh
Hi Josh,
You will find that some patterns can be combined and others not so much—depending very much on the application. Keep in mind that the pattern is designed to meet development goals and maintain good OOP principles. The important criteria is whether the combinations (of design patterns) work to enhance re-use and change. Sometimes mixing design patterns works fine as you will see in the Model View Controller (MVC), while in other cases the components conflict with one another. My advice is to get the basics of each design pattern first, and then you can experiment with mixing designs if your task requires it.
Kindest regards,
Bill
So how does this work with Beans? I am trying to set up a Decorator pattern where my Concrete Component is an Actionscript class that is a Bean (has true getters and setters). I am doing this because I have a Coldfusion component returning a custom object (cfcdirectory.myBean) that I am casting to my Concrete class of type cfcdirectory.myBean. That part works and is helpful so my CF developer and I can reuse beans in other projects.
Now I want to use a Decorator pattern to add new functionality that is relevant to my Flex app only. So I have an “AbstractBean” class that looks like this:
——————————————
An abstract decorator that looks like:
——————————————
This is what my Concrete Component looks like (this is the Bean that maps one to one to my CFC return object):
——————————————
And finally the Concrete Decorator where I want to add a new Property based on the Display Value:
I am getting nasty errors saying I am overridding classes that can’t be overridden (the actual getter / setter functions). I can get my bean data to populate if I monkey with this but I can never seem to get the “NewValue” to actually populate. Can you point me to an example where somebody does the Decorator with a Bean style concrete component please? I would really like to get this to work. Thanks!!
Hi Bill,
I know this post is history but I am a slow student and I can only study design patterns during my holidays.
I am using a decorator pattern for a small drag and drop language exercise. I have a component class with basic dragdrop funcionality and then I decorate it with sound ,some embellisments(filters) and correct/incorrect feedback(all in diferent concrete decorator classes).
The app works fine but I have found something weird: if I place the objects on the stage before I decorate them, everything goes fine, but if I place them after decorating them I get strange behaviours.
Could we have a post on how to place decorated items on the stage?
Thank you
Curro
Hi Curro,
That’s a great question, and no posts are history on this blog. (That’s because, they’re based on topic; not timeframe.) For me, one of the trickiest problems in using ActionScript 3.0 is using the DisplayObjectContainer effectively. Further, this is not just an issue for the Decorator but for all design patterns for both Flash and Flash Builder (Flex).
In looking at the Client class in the above example, I believe that you can find the answer. What is being added to the DisplayObjectContainer (stage) is the “decorated object” not just the “object” and the “decoration” as separate elements.
The Decorator design pattern is the only one I know that has an abstract class that is a subclass. So what I believe you’ve attempted to do and why you’re running into trouble is that you’ve separated the decorator from the component and because the decorator is subclassed from the component, it’s causing a problem with the DisplayObjectContainer. Of course, I’d have to look at the code, but that’s what it sounds like from what you say.
Take a look at the documentation for the DisplayObject and DisplayObjectContainer.
Kindest regards,
Bill
Hi Bill,
I do have a common ancestor class for both the concrete component and the abstract decorator. So I don’t think that’s the problem.
Anyway, I didn’t express myself clearly: the strange behaviours happen or not depending on when I set the X and
Y coordinates of the object(before or after the decoration) not on when I add them to the stage.
I also forgot to mention that I am using a strategy pattern to implement different types of checking.
Shall I send you or post the files? They are 15 although short!
Thank you again for your time and interest.
Curro
Hi Curro,
Okay, now that makes it a lot clearer. So the problem is that the x and y properties are acting strangely. Why not send them and we’ll see what’s going on. Just put them in one big comment, and I’ll break them down so that we can look at the code and figure out what’s going on.
Take care,
Bill
Hi again Bill,
here it goes! You will also need two mp3 files named nice.mp3 and horrible.mp3 in a folder named audio. In my app these contain the pronunciation of the words.
You will also need a fla file with a rectanguar movieclip (extending sprite) named “tg” associated with the class Target.
In the real application I have an array of words and one of targets(normally photographs or drawings representing the words).
Thank you for your help and patience
Curro
/*
The component class:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.*;
import flash.events.Event;
public class DragQuestion extends Sprite
{
protected var initX;
protected var initY;
private var userAnswer:*;
private var answer:*;
private var isRight:Boolean;
private var checkStrategy:CheckStrategy;
private var base:Sprite = new Sprite();
public function setSprite(sprite:Sprite)
{
base = sprite;
addChild(base);
}
public function dragMovie(event:MouseEvent):void
{
this.startDrag();
}
public function dropMovie(event:MouseEvent):void
{
this.stopDrag();
checkTarget();
}
public function setStrategy(checkStrategy:CheckStrategy)
{
this.checkStrategy = checkStrategy;
this.checkStrategy.setTarget(this);
}
public function setTarget(t:*)
{
this.answer = t;
}
public function getAnswer():*
{
return this.answer;
}
public function get origX():Number
{
return initX;
}
public function set origX(n:Number)
{
this.initX = n;
}
public function get origY():Number
{
return initY;
}
public function set origY(n:Number)
{
this.initY = n;
}
public function set correct(b:Boolean)
{
this.isRight = b;
}
public function get correct():Boolean
{
return this.isRight;
}
protected function checkTarget()
{
checkStrategy.checkTarget();
this.isRight = this.isRight;
}
}
}
******************
The Abstract Decorator class:
package
{
import flash.events.MouseEvent;
import flash.media.Sound;
public class DrgQDecorator extends DragQuestion
{
public function setSound(s:Sound):void{}
override public function dragMovie(e:MouseEvent):void{}
override public function dropMovie(e:MouseEvent):void{}
}
}
************
The concrete component class:
package
{
public class DragTextQuestion extends DragQuestion
{
private var word:String;
public function setUp(word:String, target:*, checkStrategy:CheckStrategy)
{
this.word = word;
this.buttonMode = true;
setSprite( new DragForm(word));
setStrategy(checkStrategy);
setTarget(target);
}
}
}
***************
Check strategy interface:
package
{
public interface CheckStrategy
{
function checkTarget():void;
function setTarget(target:Object):void;
}
}
**************
Concrete Strategy 1:
package
{
import flash.geom.*;
public class LooseCheck implements CheckStrategy
{
private var question:DragQuestion;
public function setTarget(target:Object):void
{
this.question = target as DragQuestion;
}
public function checkTarget():void
{
if (question.dropTarget != null)
{
if (question.dropTarget.parent.hasOwnProperty(”isTarget”))
{
if(question.dropTarget.parent == question.getAnswer())
{
question.correct =true;
}
else
{
question.correct =false;
}
question.x = question.dropTarget.parent.x;
question.y = question.dropTarget.parent.y;
}
else
{
question.x = question.origX;
question.y = question.origY;
}
}
else
{
question.x = question.origX;
question.y = question.origY;
question.correct = false;
}
trace(”Is Right?: ” + question.correct);
}
}
}
********************
Concrete Check strategy 2:
package
{
public class TightCheck implements CheckStrategy
{
private var question:DragQuestion;
public function setTarget(target:Object):void
{
this.question = target as DragQuestion;
}
public function checkTarget():void
{
if (question.hitTestObject(question.getAnswer()))
{
question.correct = true;
question.x = question.dropTarget.parent.x;
question.y = question.dropTarget.parent.y;
}
else
{
question.correct = false;
question.x = question.origX;
question.y = question.origY;
}
trace(question.correct);
}
}
}
*********************
Concrete Decorator 1:
package
{
import flash.events.MouseEvent;
import flash.media.Sound;
import flash.net.URLRequest;
public class DrgQSoundDecorator extends DrgQDecorator
{
private var dragQ:DragQuestion;
private var sound:Sound;
public function DrgQSoundDecorator(o:DragQuestion)
{
this.dragQ = o;
addChild(dragQ);
}
override public function dragMovie(e:MouseEvent):void
{
this.dragQ.dragMovie(e);
}
override public function dropMovie(e:MouseEvent):void
{
sound.play();
this.dragQ.dropMovie(e);
}
override public function get correct():Boolean
{
return this.dragQ.correct;
}
override public function setSound(s:Sound):void
{
this.sound = s;
}
}
}
****************
Concrete Decorator 2:
package
{
import flash.events.MouseEvent;
import flash.filters.*;
public class DrgQFilterDecorator extends DrgQDecorator
{
private var dragQ:DragQuestion;
var bevelFilter:BevelFilter = new BevelFilter();
var shadowFilter:DropShadowFilter = new DropShadowFilter();
var glowFilter:GlowFilter = new GlowFilter();
public function DrgQFilterDecorator(o:DragQuestion)
{
this.dragQ = o;
bevelFilter.shadowAlpha =0.5;
bevelFilter.distance = 2;
dragQ.filters = [bevelFilter, shadowFilter];
addChild(dragQ);
}
override public function dragMovie(e:MouseEvent):void
{
glowFilter.color = 0xAA0000;
glowFilter.blurX = 32;
glowFilter.blurY = 32;
glowFilter.strength = .6;
dragQ.filters = [glowFilter];
this.dragQ.dragMovie(e);
}
override public function dropMovie(e:MouseEvent):void
{
this.dragQ.filters =[bevelFilter, shadowFilter];
this.dragQ.dropMovie(e);
}
override public function get correct():Boolean
{
return this.dragQ.correct;
}
}
}
*****************
Concrete Decorator 3:
package
{
import flash.events.MouseEvent;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.display.*;
import flash.geom.Point;
import flash.events.MouseEvent;
//import flash.filters.DropShadowFilter
public class DrgQCorrectDecorator extends DrgQDecorator
{
private var dragQ:DragQuestion;
private var sound:Sound;
var figure:Shape = new Shape();
public function DrgQCorrectDecorator(o:DragQuestion)
{
this.dragQ = o;
addChild(dragQ);
var rect = getBounds(dragQ);
var pt:Point = new Point(rect.x +rect.width – 10, rect.y + 10)
//figure.graphics.beginFill();
figure.graphics.lineStyle(4,0×33CC33);
figure.graphics.moveTo(pt.x,pt.y);
var pt2:Point = new Point(pt.x + 8, pt.y -8);
figure.graphics.lineTo(pt2.x,pt2.y);
var pt3 = new Point(pt.x – 5, pt.y -5);
figure.graphics.moveTo(pt.x, pt.y);
figure.graphics.lineTo(pt3.x, pt3.y);
dragQ.addChild(figure);
trace(”getBounds(dragQ)” + getBounds(dragQ));
trace(”getBounds(this)” + getBounds(this));
this.addEventListener(MouseEvent.MOUSE_DOWN, dragMovie);
}
override public function dragMovie(e:MouseEvent):void
{
try{
dragQ.removeChild(figure);
}
catch(e:Error)
{
}
this.dragQ.dragMovie(e);
}
override public function dropMovie(e:MouseEvent):void
{
this.dragQ.dropMovie(e);
}
override public function get correct():Boolean
{
if(this.dragQ.correct)
{
this.dragQ.removeEventListener(MouseEvent.MOUSE_DOWN, dragMovie);
this.dragQ.buttonMode = false;
}
return this.dragQ.correct;
}
}
}
************************+
Concrete Decorator 4:
package
{
import flash.events.MouseEvent;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.display.*;
import flash.geom.Point;
import flash.events.MouseEvent;
public class DrgQWrongDecorator extends DrgQDecorator
{
private var dragQ:DragQuestion;
private var sound:Sound;
var figure:Shape = new Shape();
public function DrgQWrongDecorator(o:DragQuestion)
{
this.dragQ = o;
addChild(dragQ);
var rect = getBounds(dragQ);
var pt:Point = new Point(rect.x +rect.width – 10, rect.y + 10)
figure.graphics.lineStyle(4,0xFFBB00);
figure.graphics.moveTo(pt.x,pt.y);
var pt2:Point = new Point(pt.x + 8, pt.y -8);
figure.graphics.lineTo(pt2.x,pt2.y);
var pt3 = new Point(pt.x , pt.y-8 );
figure.graphics.moveTo(pt.x+8, pt.y);
figure.graphics.lineTo(pt3.x, pt3.y);
dragQ.addChild(figure);
this.addEventListener(MouseEvent.MOUSE_DOWN, dragMovie);
}
override public function dragMovie(e:MouseEvent):void
{
try{
dragQ.removeChild(figure);
}
catch(e:Error)
{
}
this.dragQ.dragMovie(e);
}
override public function dropMovie(e:MouseEvent):void
{
this.dragQ.dropMovie(e);
}
override public function get correct():Boolean
{
return this.dragQ.correct;
}
}
}
*******************
Target Class:
package
{
import flash.display.Sprite;
public class Target extends Sprite
{
public var isTarget:Boolean = true;
}
}
***************************
Helper class to give form to dragable words:
package
{
import flash.text.*;
import flash.display.Shape;
import flash.display.Sprite;
public class DragForm extends Sprite
{
private var word:String;
private var field:TextField;
public function DragForm(word:String)
{
this.word = word;
display();
}
public function setUp()
{
}
private function display()
{
field = new TextField();
field.multiline = false;
field.selectable = false;
field.autoSize = TextFieldAutoSize.CENTER;
field.type = TextFieldType.DYNAMIC;
field.text = word;
//field.background = true;
//field.border = true;
var format:TextFormat = new TextFormat();
format.font = “Arial”;
format.size = 18;
format.color = 0xFFFFFF;
format.bold = true;
field.setTextFormat(format);
var shape:Shape = new Shape();
var bgColour:uint = 0xCC3300;
shape.graphics.beginFill(bgColour);
shape.graphics.drawRoundRect(- field.width/2 -2, (-field.height/2)-2 ,field.width +4 ,field.height+4 ,30);
shape.graphics.endFill();
addChild(shape);
addChild(field);
field.x = – field.width / 2;
field.y = – field.height / 2;
var theMask = new Sprite();
theMask.graphics.beginFill(0×000000, 0.0);
theMask.graphics.drawRect(-field.width/2,-field.height/2,field.width,field.height);
theMask.graphics.endFill();
addChild(theMask);
}
}
}
********************
finally the client class. It has only two drag questions with a different check strategy each. One(TightCheck) doesn’t let the dragWord on the target unless it is the right one. The other lets it stay on and you see if it is correct with visual clues.
package
{
import flash.text.*;
import flash.display.Shape;
import flash.display.Sprite;
public class DragForm extends Sprite
{
private var word:String;
private var field:TextField;
public function DragForm(word:String)
{
this.word = word;
display();
}
public function setUp()
{
}
private function display()
{
field = new TextField();
field.multiline = false;
field.selectable = false;
field.autoSize = TextFieldAutoSize.CENTER;
field.type = TextFieldType.DYNAMIC;
field.text = word;
//field.background = true;
//field.border = true;
var format:TextFormat = new TextFormat();
format.font = “Arial”;
format.size = 18;
format.color = 0xFFFFFF;
format.bold = true;
field.setTextFormat(format);
var shape:Shape = new Shape();
var bgColour:uint = 0xCC3300;
shape.graphics.beginFill(bgColour);
shape.graphics.drawRoundRect(- field.width/2 -2, (-field.height/2)-2 ,field.width +4 ,field.height+4 ,30);
shape.graphics.endFill();
addChild(shape);
addChild(field);
field.x = – field.width / 2;
field.y = – field.height / 2;
var theMask = new Sprite();
theMask.graphics.beginFill(0×000000, 0.0);
theMask.graphics.drawRect(-field.width/2,-field.height/2,field.width,field.height);
theMask.graphics.endFill();
addChild(theMask);
}
}
}
*/
I forgot; you will also need a button named “checkButton” on the fla.
Hi again,
I’ve seen I posted the DragForm class twice instead of the client class:
Here it goes:
package
{
import flash.display.*;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.events.MouseEvent;
import flash.events.IOErrorEvent;
public class Client extends Sprite
{
private var aTargets:Array = [];
private var aDrags:Array = [];
public function Client()
{
var tgt1 = new tg();// a movie clip associated with the Target class
tgt1.x = 100;
tgt1.y = 150;
addChild(tgt1)
aTargets.push(tgt1);
var drag1 = new DragTextQuestion();// DragTextQuestion is a concrete component
drag1.setUp(”nice”, tgt1, new LooseCheck);// We need a word, a target and a check strategy
/******** This is what makes it function properly ****
if I place this code after the decorations things go wrong *****/
drag1.x = drag1.origX = 300;
drag1.y = drag1.origY = 150;
/*******/
// preparing for the sound decorator
var sound = new Sound(new URLRequest(”audio/nice.mp3″));
drag1 = new DrgQSoundDecorator(drag1);
drag1.setSound(sound);
// second decoration (filters)
drag1 = new DrgQFilterDecorator(drag1);
// launch question
drag1.addEventListener(MouseEvent.MOUSE_DOWN, drag1.dragMovie);
drag1.addEventListener(MouseEvent.MOUSE_UP, drag1.dropMovie);
//get the drag into an array for later adding to stage and checking
aDrags.push(drag1);
// Second drag object will have a different checking strategy
var tgt2 = new tg();// a movie clip associated with the Target class
tgt2.x = 100;
tgt2.y = 250;
aTargets.push(tgt2);
var drag2 = new DragTextQuestion();// DragTextQuestion is a concrete component
drag2.setUp(”horrible”, tgt2, new TightCheck);// We need a word, a target and a check strategy
drag2.x = drag2.origX = 300;
drag2.y = drag2.origY = 250;
// preparing for the sound decorator
var sound2 = new Sound(new URLRequest(”audio/horrible.mp3″));
drag2 = new DrgQSoundDecorator(drag2);
drag2.setSound(sound2);
// second decoration (filters)
drag2 = new DrgQFilterDecorator(drag2);
// launch question
drag2.addEventListener(MouseEvent.MOUSE_DOWN, drag2.dragMovie);
drag2.addEventListener(MouseEvent.MOUSE_UP, drag2.dropMovie);
//get the drag into an array for later checking
aDrags.push(drag2);
// Add targets to stage
// set up for checking
checkButton.addEventListener(MouseEvent.CLICK, checkH);
placeTargets();
placeDrags();
}
function checkH(e:MouseEvent)
{
for (var i = 0; i < aDrags.length; i++)
{
if (aDrags[i].correct == false)
{
aDrags[i] = new DrgQWrongDecorator(aDrags[i]);
addChild(aDrags[i]);
}
else
{
aDrags[i] = new DrgQCorrectDecorator(aDrags[i]);
addChild(aDrags[i]);
}
}
}
private function placeTargets()
{
for (var i = 0; i < aTargets.length; i++)
{
addChild(aTargets[i]);
}
}
private function placeDrags()
{
for (var i = 0; i < aDrags.length; i++)
{
addChild(aDrags[i]);
}
}
}
}