Home Tutorials Forums Articles Blogs Movies Library Employment Press

Go Back   ActionScript.org Forums > General > Best Practices

Reply
 
Thread Tools Rate Thread Display Modes
Old 05-25-2009, 01:11 AM   #1
Flash Gordon
rather be programming
 
Flash Gordon's Avatar
 
Join Date: Feb 2005
Location: City of Angels
Posts: 10,140
Default [example] Design Patterns:Decorators

This isn't a question but just an example of a pattern I've been finding really helpful lately and it's the awesome decorator pattern. It's a pattern I've neglected a lot until as of late. It's seems to have become my best friend now. I use it to hide asynchronous behavior and to add other "common" behavior.

So why am I writing this? Well, I think using the decorator pattern on Duck, Geese, and Coffee rather sucks, don't you? (Sorry Head First, I love you guys but thought you could use some help). I've never made a Duck in Flash and never will. However, I make sliders, programmatic skins (which I have decorators for resizing behavior), data controllers, centering scripts all of which use decorators.

I've written an example (attached) of a very common use that I have for decorators and that I think you all should have as well. It comes in the form of a slider control. Sliders can be used for a bunch of things: your scrollbar slider to the right there >>>, a volume slider, a panning slider, etc. The problem I was having was for a volume slider, the typical set up is the bottom of the slider is the min value and the top is the max value. Well, this worked great for me for volume controls but for a slider that controlled scrolling, it was completely backwards. I could either invert the values everywhere I needed it or add a property of ALL of my sliders to toggle an inversion for me.....or....I could add write 1 decorator that would invert all the values for me. This puts my logic all in 1 location rather than several and leaves my original class closed from modification. So here's what I did and it's pretty simple:

I first made an AbstractSliderDecorator so that I could make many decorators for my sliders (and I did) which would make it easier to make the decorators. All i did was delegate all of my property calls to a slider passed into the constructor.
ActionScript Code:
package com.fg.controls {     import flash.events.EventDispatcher;         import com.fg.events.SliderEvent;         public class AbstractSliderDecorator extends EventDispatcher implements ISlider     {         private var slider:ISlider;                 public function get minValue():Number { return slider.minValue; }         public function set minValue(value:Number):void { slider.minValue = value; }                 public function get maxValue():Number { return slider.maxValue; }         public function set maxValue(value:Number):void { slider.maxValue = value; }                 public function get value():Number { return slider.value; }         public function set value(value:Number):void { slider.value = value; }                 public function AbstractSliderDecorator(slider:ISlider):void         {             this.slider = slider;             this.slider.addEventListener(SliderEvent.SLIDER_CHANGE, bubbleHandler, false, 0, true);         }                 private function bubbleHandler(e:SliderEvent):void         {             dispatchEvent( e );         }     } }

So then I made my InvertedSliderDecorator which was pretty easy now. All I didn't was overrode the value properties to give the appearance the value is inverted. Which I thought was pretty slick.
ActionScript Code:
package com.fg.controls {     import flash.events.EventDispatcher;         import com.fg.events.SliderEvent;         public class InvertedSliderDecorator extends AbstractSliderDecorator     {         private var slider:ISlider;                 override public function get value():Number { return invertValue(slider.value); }         override public function set value(value:Number):void { slider.value = invertValue(value); }                 public function InvertedSliderDecorator(slider:ISlider):void         {             super(slider);             this.slider = slider;             addEventListener(SliderEvent.SLIDER_CHANGE, sliderChangeHandler, false, 1, true);         }                 private function invertValue(value:Number):Number         {             var difference:Number = value - slider.minValue;             return slider.maxValue - difference;         }                 private function sliderChangeHandler(e:SliderEvent):void         {             e.value = invertValue(e.value);         }     } }

And lastly here is my slider class. This class is a sample of my slider. It SHOULD be abstracted out and I hinted at where that should be by exposing protected methods. The only difference between a HorizontalSlilder and VeritcalSlider could be the commits to the value property.
ActionScript Code:
package com.fg.controls {     import flash.display.DisplayObject;     import flash.display.Shape;     import flash.display.Sprite;     import flash.events.Event;     import flash.events.EventDispatcher;     import flash.events.MouseEvent;     import flash.geom.Rectangle;         import com.fg.events.SliderEvent;         public class VerticalSlider extends EventDispatcher implements ISlider     {         private var dummy:Shape;                 private function get limitsChange():Number { return maxValue - minValue; }                 private var _handle:Sprite;         public function get handle():Sprite { return _handle; }                 private var _track:DisplayObject;         public function get track():DisplayObject { return _track; }                 private var _bounds:Rectangle;         public function get bounds():Rectangle { return _bounds; }         public function set bounds(value:Rectangle):void         {             if (_bounds && _bounds.equals(value)) return;             _bounds = value;         }                 private var _minValue:Number = 0;         public function get minValue():Number { return _minValue; }         public function set minValue(value:Number):void         {             _minValue = value;         }                 private var _maxValue:Number = 1;         public function get maxValue():Number { return _maxValue; }         public function set maxValue(value:Number):void         {             _maxValue = value;         }                 private var _value:Number;         public function get value():Number { return _value; }         public function set value(value:Number):void         {             if (_value == value) return;             _value = value;             commitValueProperty();             dispatchEvent( new SliderEvent(SliderEvent.SLIDER_CHANGE, this.value) );         }                 public function VerticalSlider()         {             super();             dummy = new Shape();         }                 public function initAssets(handle:Sprite, track:DisplayObject):void         {             _handle = handle;             _track = track;                         handle.buttonMode = true;             handle.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownHandler, false, 0, true);             handle.addEventListener(MouseEvent.MOUSE_UP, handleMouseUpHandler, false, 0, true);                         bounds = track.getBounds(handle.parent);             bounds.height -= handle.height;             bounds.width = 0;             bounds.x = handle.x;         }                 protected function commitValueProperty():void         {             var percent:Number = 1-(value - minValue) / limitsChange             var location:Number = percent * bounds.height + bounds.y;             handle.y = location;         }                 protected function calculateValue():void         {             var percent:Number = 1-(handle.y - bounds.y) / bounds.height;             var calculatedValue = percent * limitsChange + minValue;             this.value = calculatedValue;         }                 protected function handlePressed():void         {             handle.startDrag(false, bounds);             dummy.addEventListener(Event.ENTER_FRAME, dummyEnterFrameHandler, false, 0, true);         }                 protected function handleReleased():void         {             handle.stopDrag();             dummy.removeEventListener(Event.ENTER_FRAME, dummyEnterFrameHandler);         }                 private function dummyEnterFrameHandler(e:Event):void         {             calculateValue();         }                 private function handleMouseDownHandler(e:MouseEvent):void         {             handlePressed();             handle.stage.addEventListener(MouseEvent.MOUSE_UP, handleMouseUpHandler, false, 0, true);         }                 private function handleMouseUpHandler(e:MouseEvent):void         {             handleReleased();             handle.stage.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUpHandler);         }             }     }
ActionScript Code:
package com.fg.controls {     import flash.events.IEventDispatcher;         public interface ISlider extends IEventDispatcher     {         function get minValue():Number;         function set minValue(value:Number):void;                 function get maxValue():Number;         function set maxValue(value:Number):void;                 function get value():Number;         function set value(value:Number):void;             } }

If you look at the attached fla/swf you'll see two sliders: one has the decorator on it and the other does not. I could now use my decorator on an HorizontalSlider as well as my VerticalSlider (or even an crazy LoopDeLoopSlider). Same code reused and easily maintainable. And there are many other decorators that could be applied to a slider such as a EvenValueSliderDecorator for instance.

THANKS DECORATORS!
__________________
trace("Good bye Flash.") Log.i(TAG, "Hello Droid");
Flash Gordon is offline   Reply With Quote
Old 05-25-2009, 02:03 AM   #2
wvxvw
Holosuit User
 
wvxvw's Avatar
 
Join Date: Oct 2006
Location: Tel Aviv
Posts: 4,301
Send a message via ICQ to wvxvw
Default

Mmmm... I think there was a discussion on this very same issue before... well, now I just happen to have a better example of what I was trying to say then.
What you are using is Inheritence pattern, which doesn't make it worse or better then Decorator, but it's just different... The point of decorator is the ability to create it at runtime, not to have a precompiled one...

If you don't mind looking through quite a bit of not polished code, here's an example, of what may be classified as decorator:
this is a class that extends Sound and it is created at runtime from the externally loaded resource and randomply generated descriptor:
http://code.google.com/p/e4xu/source...vxvws/encoding

First see what SWFCompiler class does in compileMP3SWF() method, then see the MP3Transcoder class and SWFTag + extending classes in the tag folder.
The use of the class may be found here:

http://code.google.com/p/e4xu/source...dCompiler.mxml

This is a dummy AIR application that I used for testing this class. I have also a production variant of this, but it is closely bound to my other project and it's not in the SVN, so I cannot show the entire flow, but, hope you can guess what it does in the end. (Essentially it than calls Loader#loadBytes(bytesOfCompiledSWF) and grabs the new class definition from Loader#content, which allows me later to instantiate a new generated class).

Sorry to be a bore...
__________________
The .NET open source editor for Flash and web developers
*This would be my contribution to the project*
couchsurfing if you need it
wvxvw is offline   Reply With Quote
Old 05-25-2009, 02:58 AM   #3
Flash Gordon
rather be programming
 
Flash Gordon's Avatar
 
Join Date: Feb 2005
Location: City of Angels
Posts: 10,140
Default

Sorry bro, as I've pointed out to you before (in a more subtle fasion) you're description of the Decorator pattern is simply incorrect and even Michael (post 5 & 6) (the man at patterns) agrees and so does the GoF and Head First.

Trying reading http://www.as3dp.com/?s=decorator ActionScript 3.0 Easy and Practical Decorator Design Pattern post (or wikipedia) to get a clearer explanation of the decorator pattern. The intention of my post is not to describe what the decorator pattern is to anyone but to show a practical application of how to use it the Flash world which Head First fails to do.

I understand you'll never agree with me on this and I don't expect you to. However, you can help by starting your own thread about the great deception of the decorator pattern that GoF, Head First, as3 design pattern, wikipedia, and me have all bought into. Sorry to be so blatant, but it gets tiring tip-toeing around issues. Even with that said, i wishing you the best in the flash world, we're still friends, and I know you're a good programmer.
__________________
trace("Good bye Flash.") Log.i(TAG, "Hello Droid");
Flash Gordon is offline   Reply With Quote
Old 05-25-2009, 10:21 AM   #4
wvxvw
Holosuit User
 
wvxvw's Avatar
 
Join Date: Oct 2006
Location: Tel Aviv
Posts: 4,301
Send a message via ICQ to wvxvw
Default

Quote:
The decorator pattern can be used to make it possible to extend (decorate) the functionality of a class at runtime.
This is the quote from the wiki link you just gave...
And thanks for the compliments, so far I'm posting a lot, I know that a t average every 100th post of mine is a total nonsense... well, maybe this one happen to be 101st
__________________
The .NET open source editor for Flash and web developers
*This would be my contribution to the project*
couchsurfing if you need it
wvxvw is offline   Reply With Quote
Old 05-25-2009, 08:08 PM   #5
rrh
throw a trace() in there
 
Join Date: Dec 2006
Posts: 1,982
Default

That quote might be a poor choice of words.

This is how I understand it:
ActionScript Code:
//slider2 is undecorated slider2 = new InvertedSliderDecorator(slider2); //slider2 is decorated

With ordinary inheritance, it would determine the functionality of slider2 when it was instantiated. With the application of a decoration, the functionality of slider2 can be extended at any time.

So I don't think the distinction is runtime vs. compile time, but at instantiation vs. after instantiation.

The other value of a decorator is multiple decorators can be applied to the same instance in any combination.
rrh is offline   Reply With Quote
Old 05-25-2009, 08:28 PM   #6
Flash Gordon
rather be programming
 
Flash Gordon's Avatar
 
Join Date: Feb 2005
Location: City of Angels
Posts: 10,140
Default

::nods::

Quote:
Originally Posted by me
I've been wrong many times before, but as far as I'm aware classes can not be created at run time. Classes are created at author/compile time, objects are created at run time. I do see any benefit of creating classes at run time. Even the xml which would describe a class is created at author (edit: changed from other) time and then at compile time and would still be nothing more than a convenience language like mxml.

Decorator are a way of dynamically adding behavior to an object and having that object "look" the same way but behave (slightly) different.
It's a difference in terminology that's the confusing part. I would consider this runtime or dynamically modifiying the behavior of slider
ActionScript Code:
var slider:ISlider = new VerticalSlider(); if (foobar) slider = new InvertedSliderDecorator(slider); if (fudcake) slider = new EvenValueSliderDecorator(slider);
but i think rrh said it pretty well. Something to remember when trying to identify a pattern is not how the objects interacts (the uml pattern) but what the intention is.

But let me finish the quote started above
Quote:
Originally Posted by wiki
The decorator pattern can be used to make it possible to extend (decorate) the functionality of a class at runtime. This works by adding a new decorator class that wraps the original class......

Subclassing adds behavior at compile time whereas decorating can provide new behaviour at runtime.
(p.s...sorry or being so darn forward wvxvw...roomates suck, it effects life, yada yada I'm sure you understand..urgh)
__________________
trace("Good bye Flash.") Log.i(TAG, "Hello Droid");
Flash Gordon is offline   Reply With Quote
Old 05-25-2009, 11:43 PM   #7
MichaelxxOA
Flash Sucks
 
MichaelxxOA's Avatar
 
Join Date: Mar 2005
Location: Victorville, Ca
Posts: 2,228
Send a message via AIM to MichaelxxOA Send a message via MSN to MichaelxxOA Send a message via Yahoo to MichaelxxOA Send a message via Skype™ to MichaelxxOA
Default

I doubt I'm going to add any clarification to this ... but I do have, what I believe, is a decent example of decorator.

I think (I and think being the operative words here) you are both right. I think that wvxvw's quote says it best.

Quote:
The decorator pattern can be used to make it possible to extend (decorate) the functionality of a class at runtime.
Anything that satisfies that would seem, to me, to be a decorator.

Having said that, here is one way to use this idea.

Create one interface for how you want to iterate over collections of objects.

http://aidlia.com/resources/decorator/Iterator.as.txt

Define two collections of objects and how they can be iterated (I chose the Array and DisplayList).

http://aidlia.com/resources/decorato...terator.as.txt
http://aidlia.com/resources/decorato...terator.as.txt

Now when you are iterating you are working at a higher level of abstraction. This means that things you do when iterating can now be thought of on this level. For example, how can you iterate through only specific types of objects?

http://aidlia.com/resources/decorato...terator.as.txt

^ and that is your decorator.

Here are two instances where this decorator is being used (in real production code):

http://aidlia.com/resources/decorator/Sequence.as.txt
http://aidlia.com/resources/decorato...Element.as.txt

Hope that helps, great stuff FB and wvxvw.
MichaelxxOA is offline   Reply With Quote
Old 05-25-2009, 11:49 PM   #8
wvxvw
Holosuit User
 
wvxvw's Avatar
 
Join Date: Oct 2006
Location: Tel Aviv
Posts: 4,301
Send a message via ICQ to wvxvw
Default

FG:
I live alone in the center of the city, palms and other evergreen plants, tropical fruits etc... I can see the sea from the the balcony of my room, and it takes me less then 5 minutes to walk to the beach and, in general, I enjoy life... mmm... besides few minor disadvantages of my location it's pretty cool here

But! And I really don't intend to harm your feelings and hope you will accept my critics in good spirit, or, at least, suppose I just blubber because I like to

If you would look into my example you'd see that in my case the class is being truly generated at runtime, and it does what the decorator is meant for, while, in your example you're achivening the same goal, but, you have your class precompiled, which is an essential difference between what Inheritance, Subclassing, Interface on one side and Decorator on the other side are.
You just take it for granted that AS3 classes cannot be generated at runtime. Here I would partially agree, because it is not a trivial task, but it is not completely impossible - the proof is in my first post.

If you'll read until this line... and you don't mind a lot more reading - see this example of what is realy a whatmacallit "classical" example of Decorator:
http://209.85.129.132/search?q=cache...&ct=clnk&gl=il
(if the link doesn't show, thy this: httpdyn.cs.huji.ac.il/moodles/cs08/file.php/67125/Lectures/OOP_Streams_2.ppt )

I.e. if it was possible to do in AS3, it would look something like this:

ActionScript Code:
var someSlider:Slider = new {VerticalSlider & Slider}();
Well, I know this isn't a legit code... but it's the best I could invent to illustrate the idea...
__________________
The .NET open source editor for Flash and web developers
*This would be my contribution to the project*
couchsurfing if you need it
wvxvw is offline   Reply With Quote
Old 05-26-2009, 12:07 AM   #9
MichaelxxOA
Flash Sucks
 
MichaelxxOA's Avatar
 
Join Date: Mar 2005
Location: Victorville, Ca
Posts: 2,228
Send a message via AIM to MichaelxxOA Send a message via MSN to MichaelxxOA Send a message via Yahoo to MichaelxxOA Send a message via Skype™ to MichaelxxOA
Default

Hm, IMO you are getting caught up in details. I agree that your implementation is a decorator. I just don't agree that it's the only way to implement decorator.

FB's presentation of decorator is even actually pretty much inline with the GOF definition.
MichaelxxOA is offline   Reply With Quote
Old 05-26-2009, 12:14 AM   #10
Flash Gordon
rather be programming
 
Flash Gordon's Avatar
 
Join Date: Feb 2005
Location: City of Angels
Posts: 10,140
Default

no worries.

Call it what you want, but be aware you are going against common convention of what the community and the founders of design patterns have termed it. 3 people in this thread alone, as3dp (adobe), GoF, and Head First all call this a Decorator. Have read any of these? I've given 3 links and 5 sources that all agree with this. The only example you can give me are ones you've created. You are choosing to be iconoclastic. In the spirit of academic truthfulness and dissemination of information you should present your ideas as such: understand them and disagree by example and other supporting documentation.

Again, nothing personal, I may just be jealous of your nice view.
__________________
trace("Good bye Flash.") Log.i(TAG, "Hello Droid");
Flash Gordon is offline   Reply With Quote
Reply


Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
one template, many looks? subquark ActionScript 1.0 (and below) 1166 10-29-2013 06:18 PM
Lead Interactive/Motion Designer Wanted! Plexipixel Projects and Positions 0 01-12-2008 12:30 AM
Color seperate parts of Design vikaspa Other Flash General Questions 4 07-09-2006 03:31 PM
Help!How to make a design page for T-shirt design website lanny Other Flash General Questions 14 03-26-2006 05:10 AM
What are good design reasons for having different levels in Flash's ActionScript? echezona Simple Stuff (Newbies) 8 05-01-2002 08:44 PM


All times are GMT. The time now is 07:04 PM.

///
Follow actionscriptorg on Twitter

 


Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Ad Management plugin by RedTyger
Copyright 2000-2013 ActionScript.org. All Rights Reserved.
Your use of this site is subject to our Privacy Policy and Terms of Use.