Saturday, August 22, 2009

Skipping Flash's Events for Cleaner Code

For some time now, I've been using a technique that allows me to skip Actionscript's events and it's really been helpful in writing clean code. Recently, prominent community developers such as Andre Michelle, Joa Ebert, Robert Penner, and Nicolas Cannasse have made solid critiques of Actionscript and generally AS3's event model makes it onto their list of grievances. Andre and Joa have even created UIEvent to help deal with event listeners.

Like many other Flash developers, when I learned about creating custom events or dispatching Flash's built-in events I started doing it a lot. But..... it can really be a pain. I find that frequently dispatching and cleaning up events really dirties my code.  It also makes it harder to read.  Also, event dispatchers can be so far removed from methods who are dependent on them, that a coder will often have to search around to find the callback. Because of this I avoid Flash events as much as possible, and here's how I do it.

[Edit] Callbacks are also substantially faster than events: Grant Skinner's recent optimization talk

If I have written this code properly, I shouldn't have to explain what is going on:
private function preloadApp() : void
appPreloader.preLoad(this, preloaderSkinSwfURL, appSkinSwfURL, pushAppSkinsIntoDataModel);

private function pushAppSkinsIntoDataModel() : void
var appSkins:ApplicationDomain = appPreloader.appSkins;
dataModel.appSkins = skinBuilder.createAndReturnSkinDictionary(appSkins);

If that is not enough info, how about a peek at the appPreloader object:
public function preLoad(preloadContainer : Sprite, preloadSwfURL : String, bigSwfURL : String, preloadCompleteCallback:Function) : void
this.preloadContainer = preloadContiner;
this.bigSwfToLoadURL = bigSwfURL;
this.preloadCompleteCallback = preloadCompleteCallback;

// other code
private function preloadCompleteDoCallback(event:Event) : void

The approach I am taking is to give an object everything it needs as well as pass it a callback function to execute when it's done. Above, when I executed appPreloader's  preload() method I gave it a reference to the method I want it to call when It's done. What is also important is that my callback function ( pushAppSkinsIntoDataModel() ) occurs directly after I tell my delegate to execute a process.

The alternative to the above would probably include having my appPreloader extend EventDispatcher, and the client class would then need to add and remove the event listener, and also still call dispose() to null references for Garbage Collection.

My goal isn't to write less code or avoid Flash's event system explicitly, it's about writing Clean Code and creating a more fluid programming experience. Almost every object I write has the same code flow. It reads like a newspaper, from top to bottom,  and each object does only one thing. Because Flash's events can be very asynchronous, developers don't know where to place their callbacks.  This often creates a scenario where I am having to jump all over a file because the developer wasn't sure where to put the event handlers.  Some developers group their event handlers all together, but this is worse because the object's entire flow has been disrupted.

We are writing code for machines AND people, don't forget that.

Flash's event model can be very, very helpful and powerful, but don't blindly follow it. When using business logic delegates, it events may not necessary. Of course, we still have to pragmatic about all of it.

Anyone else interested in writing clean code, I would suggest checking out work by Robert Martin and the Object Mentor team.