Flex Best Practices – Events

I can’t stand seeing badly created code. ESPECIALLY code that works against best practices solely for the reason that the developer felt that using “best practices” would take too long or cause too many problems.

The idea of best practices (in my opinion) are a set of rules to program by that don’t get in the way of programming while keeping a consistent, high-quality level of work while leaving minimal (to no) room for error based on coding practice not coding.

Basically, don’t keep coding around the problem and adding additional coding to fix the problem, just use best practices and more on.

Right now I am going to focus on events, arguably one of the most used set of classes in the Flex framework. For this set of examples I am going to assume that the developer knows that they have to AT LEAST extend the Event class for all events.

So, some bad code…

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.unitedmindset.events
{
    import flash.events.Event;

    public class badevent extends Event
    {
        public function badevent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);
        }
       
    }
}

Naming

Wow, this code is bad right from the start. Notice the class name badevent. In Actionscript we follow the camel case naming convention. As such the name of this event should be badEvent. Notice that each word in the name of the class should start with an upper case (with the first word starting in lower case). Now classes are special, classes start with an upper case, so the FINAL name of our event should be BadEvent.

Next…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.unitedmindset.events
{
    import flash.events.Event;

    public class BadEvent extends Event
    {
        public var extraInfo1:Object;
        public var extraInfo2:Object;
        public var extraInfo3:Object;
       
        public function BadEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);
        }
       
    }
}

Typing your variables

I’m not EVEN going to start in on the naming of properties, but I will complain about the object typing. When the AS3 compiler sees an object it will create room for it in memory, but unlike a typed object it doesn’t know how much room to make, this is bad for memory. On top of that, the compiler can’t do type checking for you at compile time, so you have a greater probability of runtime exceptions in your application.

Quick update…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.unitedmindset.events
{
    import flash.events.Event;

    public class BadEvent extends Event
    {
        public var extraInfo1:String;
        public var extraInfo2:int;
        public var extraInfo3:XML;
       
        public function BadEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);
        }
       
    }
}

Now that little type change fixes our problem of type checking and memory performance.

There are many other steps a developer must take to TRULY captialize on memory performance, this is just one drop in the bucket.

Protecting your properties

However there is another problem with the properties on this event, they are exposed to change as it moves through the application. What I mean is: As the event is fired, it is very possible that there will be multiple listeners for that event. As the event hits on handler function the handler function may update the property. Now when the event gets to the next handler function the original data from the event has changed. This is unwanted and may lead to unexpected effects in your application.

Using getters we limit the properties from changing. We also will set the properties on the constructor so that the property can only be set when the event is created.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.unitedmindset.events
{
    import flash.events.Event;
   
    public class BadEvent extends Event
    {
        private var _extraInfo1:String;
       
        public function get extraInfo1():String
        {
            return _extraInfo1;
        }
       
        private var _extraInfo2:int;
       
        public function get extraInfo2():int
        {
            return _extraInfo2;
        }
        private var _extraInfo3:XML;
       
        public function get extraInfo3():XML
        {
            return _extraInfo3;
        }
       
        public function BadEvent(type:String, extraInfo1:String, extraInfo2:int, extraInfo3:XML, bubbles:Boolean=false, cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);
            _extraInfo1 = extraInfo1;
            _extraInfo2 = extraInfo2;
            _extraInfo3 = extraInfo3;
        }
   
    }
}

There may be a time you REALLY want to have the property able to change in an event as it moves through the application. I would highly discourage this as it works against best practices. As the event bubbles the changing logic in the event may be hard for future developers to follow or even find in the code.

Please notice also that all private variables start with the underscore “_”. Using this makes it so that you don’t have to roll over each variable or look in the outline to see if it is private or not.

Clone function
As an event bubbles through the application, or if you need to make a duplicate of the event, you call the clone function. However, if we don’t state the the clone function will return your newly created event type it will, by default, return just a normal event. This is highly undesirable for your application. So next we override the clone function.

1
2
3
4
        override public function clone():Event
        {
            return new BadEvent(type, extraInfo1, extraInfo2, extraInfo3, bubbles, cancelable);
        }

You will see here that we have created a new event instance and passed the variables available to this event into the new instance. and returned that instance. Very simple bit of code that will protect your program.

Event types
Now we need to create constants to describe what action this event is representing. This is very simple and very helpful. As event listeners are ultimately looking for a string match you COULD type the string right into the listener…

1
component.addEventListener("click",_handler)

If you have a developer like me that can’t spell or adds the wrong tense, then I may accidentally do this…

1
component.addEventListener("clicked",_handler)

Now we spend all day looking for why the handler is never being called. Again, this is solved with some constants.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.unitedmindset.events
{
    import flash.events.Event;
   
    public class BadEvent extends Event
    {
        public static const CLICK_BUTTON_1:String = "clickButton1";
        public static const CLICK_BUTTON_2:String = "clickButton2";
        public static const CLICK_BUTTON_3:String = "clickButton3";
       
        private var _extraInfo1:String;
       
        public function get extraInfo1():String
        {
            return _extraInfo1;
        }
       
        private var _extraInfo2:int;
       
        public function get extraInfo2():int
        {
            return _extraInfo2;
        }
        private var _extraInfo3:XML;
       
        public function get extraInfo3():XML
        {
            return _extraInfo3;
        }
       
        public function BadEvent(type:String, extraInfo1:String, extraInfo2:int, extraInfo3:XML, bubbles:Boolean=false, cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);
            _extraInfo1 = extraInfo1;
            _extraInfo2 = extraInfo2;
            _extraInfo3 = extraInfo3;
        }
       
        override public function clone():Event
        {
            return new BadEvent(type, extraInfo1, extraInfo2, extraInfo3, bubbles, cancelable);
        }
   
    }
}

Now with the constants the intellisense will give me some assistance and I get the following code for adding my event listener. No possibility for spelling and grammar mistakes here.

1
component.addEventListener(BadEvent.CLICK_BUTTON_1,_handler)

Also notice that the constants we difine is all in upper case with underscores between the individual words. This is another best practice and camel case naming practice. Again, this just quickly alerts other users that this is a constant.

ASDoc
Finally, before we lock up any class we add ASDocs for future developers. Below is the FINAL 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.unitedmindset.events
{
    import flash.events.Event;
   
    /**
     * This event is fired when a user clicks on one of three buttons.
     * @author Administrator
     *
     */
    public class GoodEvent extends Event
    {
        public static const CLICK_BUTTON_1:String = "clickButton1";
        public static const CLICK_BUTTON_2:String = "clickButton2";
        public static const CLICK_BUTTON_3:String = "clickButton3";
       
        private var _extraInfo1:String;
        /**
         * Button label.
         * @return
         *
         */    
        public function get extraInfo1():String
        {
            return _extraInfo1;
        }
       
        private var _extraInfo2:int;
        /**
         * Button position.
         * @return
         *
         */    
        public function get extraInfo2():int
        {
            return _extraInfo2;
        }
        private var _extraInfo3:XML;
        /**
         * XML button loads.
         * @return
         *
         */    
        public function get extraInfo3():XML
        {
            return _extraInfo3;
        }
       
        /**
         * Constructor
         * @param type
         * @param extraInfo1
         * @param extraInfo2
         * @param extraInfo3
         * @param bubbles
         * @param cancelable
         *
         */    
        public function GoodEvent(type:String, extraInfo1:String, extraInfo2:int, extraInfo3:XML, bubbles:Boolean=false, cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);
            _extraInfo1 = extraInfo1;
            _extraInfo2 = extraInfo2;
            _extraInfo3 = extraInfo3;
        }
       
        /**
         * Returns new GoodEvent.
         * @return
         *
         */    
        override public function clone():Event
        {
            return new GoodEvent(type, extraInfo1, extraInfo2, extraInfo3, bubbles, cancelable);
        }
   
    }
}

Conclusion
Some people would ask why write a blog post that is so simple. I wrote this one because all the bad examples presented I have witnessed in life code over the last few weeks. I am sure that is both surprising and not surprising.

  • Share/Bookmark

Comments (15)

Jonathan CamposAugust 19th, 2009 at 12:56 pm

If you haven’t looked, also check out my MVC Best Practices: http://unitedmindset.com/jonbcampos/2009/08/18/flex-best-practices-models-views-and-controllers/

Bob TribitAugust 21st, 2009 at 11:27 am

Totally appreciate the best coding practices articles and more importantly the reasons behind them. Thank you.

Magnus LassiSeptember 6th, 2009 at 3:55 pm

I’ve run across some AS code that had custom events that did NOT override the clone function. Is there ever a valid reason to not override the clone function?

One example where I saw this is in the Netflix Library of the UsersResultEvent in the package com.netflix.webapis.users.events; http://code.google.com/p/netflix-flex-api/source/browse/trunk/netflix-flex-api/src/com/netflix/webapis/users/events/UsersResultEvent.as .

Jonathan CamposSeptember 6th, 2009 at 4:45 pm

If you found that missing it wasn’t on purpose.

When you call the base clone() method the returned event will just be a normal flash.events.Event and not the custom event you may have expected. The biggest problem is that your custom properties will be missing from the clone. Ultimately I would say no, there isn’t a good reason why you don’t override the clone() method.

RandyOctober 19th, 2009 at 11:23 am

Hi Magnus,

You don’t have to clone if you do not want the event to bubble (up through – to Application), the only parameter you need to pass to the Event super class (base Event class constructor) is type:String. So keep the event scope local when dispatching/listening to the event and you will not need clone method, so in effect have created a private event.

If you bubble up on the super class cconstructor, well you ccould see the problem, so clone is a better way to implement the bubble property.

AllynWAOctober 27th, 2009 at 2:29 pm

Great work, Jonathan…I found this really useful and well explained. Could you talk a bit about the string names you give to your event constants. Do they have to be globally-unique?

I had a recent hairball I chased for a bunch of time where I had a SiteEvent and a UserEvent that both had SAVE types. I kept getting an error thrown that a SiteEvent couldn’t be coerced to UserEvent, and nowhere could I determine that I was mixing them up in handler routines. The problem went away when I made the constants in the two classes different:

public class SiteEvent extends Event {
public static const SAVE:String = “save”;
}
public class UserEvent extends Event {
public static const SAVE:String = “save”;
}

became…

public class SiteEvent extends Event {
public static const SAVE:String = “site_save”;
}
public class UserEvent extends Event {
public static const SAVE:String = “user_save”;
}

Thanks for your thoughts.

Jonathan CamposOctober 27th, 2009 at 2:41 pm

Events don’t need to be globally unique. If I said they had to be that was a mistake.

However, I would guess what is happening is one of your components/classes are firing off an event and your metadata is no set properly or you are firing a user save event when you meant a site save event (or vica-versa).

The only time you will want to worry about a globally unique is when you are working with a global event handler or central dispatcher.

AllynWAOctober 27th, 2009 at 3:34 pm

And there you have it…my ClientDAO class has two methods:

getSitesByClientID( id:int );
getUsersByClientID( id:int );

with a common event handler:

private function getResults_success( e:ResultEvent ):void {
var token:AsyncToken = e.token;
var result:XML = e.result as XML;
clearBusyCursor();

if ( token != null ) {
switch ( token.marker ) {
case UserEvent.SAVE:
dispatchEvent( new UserEvent( UserEvent.SAVE, result ) );
break;

case SiteEvent.SAVE:
dispatchEvent( new SiteEvent( SiteEvent.SAVE, result ) );
break;
}
}
}

Hmmm…

This also ties across to your MVC article. I’m trying to overlay some of those good concepts here…I have a ways to go :-)

Thanks again.

Jonathan CamposOctober 27th, 2009 at 3:37 pm

as always, feel free to ask anything!

AshokDecember 27th, 2009 at 1:20 am

Hi Jonathan,

We are going to develop a large application which will be ported on web and desktop (AIR). can you suggest any good MVC framework for beginning with?

Jonathan CamposDecember 27th, 2009 at 12:21 pm

@ashok If you haven’t gotten into Flex/AS3 programming too much I would think Mate would help you get along rather quickly as it is mainly a tag based mvc framework. If you are feeling up on your skills, try Parsley or Robotlegs. Two frameworks that rely on IOC (Inversion of Control) and are gaining lots of popularity. Finally the de-facto is Cairngorm, a framework that most developers start. You could try also try PureMVC, though I feel it takes a lot of setup to get going. I hope that helps. These days I use each of these frameworks at some level and feel they are all strong frameworks, so I would say pick one and just get started.

Ashish DesaiMarch 18th, 2010 at 11:33 am

User FlexPMD and FlexCPD to enforce the best practices in your code base. You can configure it in flash builder and Flex Builder. Below is the link you might be interested in http://opensource.adobe.com/wiki/display/flexpmd/FlexPMD. We religiously follow these in all our development tasks.

You can create your custom rulesets to enforce more specific best practices.

Jonathan CamposMarch 18th, 2010 at 11:36 am

Completely agree. FlexPMD and FlexCPD are amazing and great tools!

ManimaranMay 14th, 2010 at 2:43 am

Can you please tell me the guidelines to setup the FlexPMD in my Flash Builder , i followed the given steps , but when i run FlexPMD it is not showing any violations in the code eventhough the code has.

Jonathan CamposMay 14th, 2010 at 8:47 am

@manimaran Have you followed these steps?: http://opensource.adobe.com/wiki/display/flexpmd/FlexPMD+Eclipse+plugin

This is a video created by Adobe that walks you through step by step. And since Flash Builder is built on Eclipse, these instructions are 100% applicable to Flash Builder.

Leave a comment

Your comment