Saturday, June 6, 2009

ditch conditionals; use factories

When I first started coding, I used a lot of switch/case statements and complex ifs. But over the years, I become suspicious of them. Now, when I feel the urge to reach for a switch/case, I slap my wrist and try to come up with a better construct.

To demonstrate the problem, I've come up with a toy example: an app that fills the stage with little Fisher-Price-type people.



There are two types of people, bald people and hairy people. Just before placing a person on the stage, my app randomly chooses if he's going to have hair or not. Bald people are all members of the Person class; Hairy folks are members of the HairyPerson class. To create such classes, I do this:

var person1 : Person = Person( new bald() );
var person2 : Person = HairyPerson( new hairy() );

Note that I type both sorts of people to Person. I can do that because HairyPerson is a subclass of Person (and subclasses can be typed to their parent's type). By giving the vars the same type, I can pass them both into functions that expect a Person type, and those functions will work regardless of whether the person is a Person or a HairyPerson.

What are those new bald() and new hairy() calls doing for me? bald and hairy are MovieClips in my FLA's Library. Person classes must be passed a reference to a MovieClip. They use the clip as base artwork, which they then customize via random colors. If you want to know more about Person construction, you can check out my FLA and AS files, but the details aren't important to this post, so I won't elaborate here.

The question is this: in my Document class, where I randomly choose types of people, how do I instantiate type chosen at random? In the past, I would have done it this way:


for ( var i : int = 0; i < 100; i++ )
{
        rand = Math.floor( Math.random() * 2 );
        
        switch ( rand )
        {
                case 0 :
                        person = new Person( new bald() );
                        break;
                        
                case 1 :
                        person = new HairyPerson( new hairy() );
                        break;
                        
                
        }
        
        person.render();
        person.x = Math.random() * Stage.stageWidth;
        person.y = Math.random() * Stage.stageHeight;
        addChild( person );
}




The problem with this is that it doesn't scale well. If I add another type of person, say a HatWearingPerson, I need to add another case -- and I need to change my Math.random() statement so that it is range includes my new case:


for ( var i : int = 0; i < 100; i++ )
{
        rand = Math.floor( Math.random() * 3 ); //2 changed to 3
        
        switch ( rand )
        {
                case 0 :
                        person = new Person( new bald() );
                        break;
                        
                case 1 :
                        person = new HairyPerson( new hairy() );
                        break;
                        
                case 2 : //added
                        person = new HatWearingPerson( new hatWearer() );
                        break;
                
        }
        
        person.render();
        person.x = Math.random() * Stage.stageWidth;
        person.y = Math.random() * Stage.stageHeight;
        addChild( person );
}



This isn't such a big deal in my toy example, but imagine a more complex system in which you were constantly adding and removing types of people. Not only would I have to create the new classes; I'd also have to remember to add (or remove) new cases to the switch/case statement and to update the Math.random() statement. Eventually -- probably sooner rather than later -- I'm going to screw up and forget to add a case or forget to update Math.random().

But the bigger problem is that my Document class and the people classes are tightly coupled. Good OOP practice tells me that those person-instantiation statements shouldn't be hard coded into the Document class. Classes should be as independent from each other as possible, so that you wind up with a modular system in which you can change one class without having to change another.

What's the alternative? Factory classes! Factories are classes that make instances of other classes: Here are mine:


package
{
        public class PersonFactory implements IPersonFactory
        {
                public function getPerson() : Person
                {
                        var person : Person = new Person( new bald() );
                        return person;
                }
        }
}



package
{
        public class HairyPersonFactory implements IPersonFactory
        {
                public function getPerson() : Person
                {
                        var person : Person = new HairyPerson( new hairy() );
                        return person;
                }
        }
}



package
{
        public class HatWearingPersonFactory implements IPersonFactory
        {
                public function getPerson() : Person
                {
                        var person : Person = new HatWearingPerson( new hatwearer() );
                        return person;
                }
        }
}



As you can see, these classes all implement an interface called IPersonFactory. You'll see how this helps me out in a second. In the meantime, here's the interface:


package
{
        public interface IPersonFactory
        {
                function getPerson() : Person;
        }
}



My new and improved Document class looks like this:


var person : Person;
var rand : int;
var factories : Array;

addFactory( new PersonFactory ); //see addFactory(), below
addFactory( new HairyPersonFactory );
addFactory( new FancyPersonFactory );

for ( var i : int = 0; i < 100; i++ )
{
        rand = Math.floor( Math.random() * factories.length );
        person = factories[ rand ].getPerson();
        
        person.render();
        person.x = Math.random() * stage.stageWidth;
        person.y = Math.random() * stage.stageHeight;
        addChild( person );
}
                        

function addFactory( factory : IPersonFactory ) : void
{
        factories.push( factory );
}



Note that the addFactory function accepts any class that implements the IFactory interface, so my Document class doesn't know -- or need to know -- how many types of people (or people factories) exists.

From now on, if I add a new Person subclass, I just need to make a factory for it and pass that factory to my the Document class.

Friday, June 5, 2009

visual test toolkit

I'm a big fan of the Flash player, the SWF format and the Actionscript language, but I'm not crazy about the Flash IDE. It belongs on the B-list of many categories.

Want an animation tool? Check out After Effects (or Toon Boom Studio). Flash is a light-weight by comparison. Want a vector-drawing app? Use Illustrator (especially now that it has the Blob Brush!). Want a coding IDE? Use Flex or FDT. Flash dips its toes into many pools but refuses to dive into any of them.

However, I do find Flash useful for one thing -- so useful that I start it up at least once a day: Flash is a faux command-line interpreter for Actionscript! It's not really an interpreter, of course. It's more of a compiler. But it beats everything else for quickly trying out a bit of Actionscript. You don't need to create a project, a class or even a function. Just throw some code on the timeline, press Command+Enter and Bob's your uncle. That's a terrible way to code anything real, but it's a great way to quickly test out a bit of functionality.

Sometimes, when I'm doing these tests, I need visual elements. And I waste time creating little circles, square and text fields. So I quit doing that. Instead, I created a little testing file that I use over and over. It's got a stage full of doodads and whatnots, and everything is labeled with its instance name. Also, items in the library have Export for Actionscript turned on, so I can create new instances with code if need be.

I leave this file open all day, with frame 1 of the timeline exposed in the AS editor. If I need to run a quick test, I'm all set to go.

Here's the file.

Tuesday, June 2, 2009

generating getters and setters

I'm down with OOP and using getters and setting instead of exposing class member variables, but making getters and setters is a pain. Flex and FTD have tools to help automate the process, but they don't automate it enough. I wanted to be able to type in a list of vars like this...

private var _newWidth : Number;
private var _newHeight : Number;
private var _scaleList : Array;
private var _moveList : Array;
private var _oldWidth : Number;
private var _oldHeight : Number;
private var _widthPercentChange : Number;
private var _heightPercentChange : Number;

... and, via a single click, get this...

public function get newWidth() : Number
{
return _newWidth;
}

public function set newWidth( value : Number ) : void
{
_newWidth = value;
}

public function get newHeight() : Number
{
return _newHeight;
}

public function set newHeight( value : Number ) : void
{
_newHeight = value;
}

public function get scaleList() : Array
{
return _scaleList;
}

public function set scaleList( value : Array ) : void
{
_scaleList = value;
}

public function get moveList() : Array
{
return _moveList;
}

public function set moveList( value : Array ) : void
{
_moveList = value;
}

public function get oldWidth() : Number
{
return _oldWidth;
}

public function set oldWidth( value : Number ) : void
{
_oldWidth = value;
}

public function get oldHeight() : Number
{
return _oldHeight;
}

public function set oldHeight( value : Number ) : void
{
_oldHeight = value;
}

public function get widthPercentChange() : Number
{
return _widthPercentChange;
}

public function set widthPercentChange( value : Number ) : void
{
_widthPercentChange = value;
}

public function get heightPercentChange() : Number
{
return _heightPercentChange;
}

public function set heightPercentChange( value : Number ) : void
{
_heightPercentChange = value;
}

And now I can, because I built a little AIR app to do it for me. I can copy in the private vars, press the GENERATE button, and -- BINGO! -- out pops my getters and setters. Then I can click the COPY button, which copies the getters and setters to the clipboard. Finally, I can paste them into Flex or FDT.



Here's a link to the app.

Notes: it is template-based, so if you don't like the code it generates, it's easy to make it do what you want. Click HELP for more info.

Also, please note that though this is a useful tool, it can be abused. For instance, some properties should be read only. They shouldn't have a setter. So use with caution and good OOP practices!

keeping up with the actionscript joneses

I use an RSS reader called Vienna to keep up with Actionscript news. Over time, I've compiled a big list of AS-related blogs, which I've now published as in opml file. Feel free to download it an import it into Vienna (File > Import). I'll update the opml when I find new feeds.

Monday, June 1, 2009

mac apps

I work on a mac. Here are the apps I use in my work. I'll update this list when I start using new apps or quit using old ones.

MEDIA
Vlc. This is a one-stop-shop video player that plays virtually any format.

WRITING
Textmate. The best text editor I've ever found for the Mac.

MacVim (free). The old Unix editor wrapped with a Mac GUI. Edit code without touching the mouse.

Open Office (free). Open-source MS Office replacement.

TextExpander. Allows you to make system-wide keyboard shortcuts that expand into blocks of text. For instance, on my system, typing aaaa expands to my complete mailing address. You can set up shortcuts that work only in specific apps.

Slidepad. Neat little text-editor that slides on and off the screen via a keyboard shortcut. I used it for on-the-fly, temporary notes.

INFO MANAGEMENT
Evernote (free). A fantastic app that lets you keep track of any sort of information. It can sync to multiple computers, the web, and many mobile devices (including the iPhone).

APP LAUNCHING
Namely (free). Allows you to launch any Mac app via the keyboard. It's a lightweight alternative to Quicksilver, which always bogs down my system.

LauncherConfig (free). Launches multiple files at once. I use it every morning to launch iChat, my mail client, my svn client, my ftp client and my Twitter client with one click.

WEB BROWSING
Firefox Browser (free). I await a Mac version of Google Chrome.

Firebug (free). THE must-have Firefox plugin for web developers.

Fluid (free). Allows you turn web pages into stand-alone apps. I use it so that I can easily launch gmail into its own window.

DEVELOPMENT
Flash Builder 4 (formerly Flex Builder). This is my main coding tool. I used to use FDT, which is terrific, but Flash Builder has now outgunned it. Flash Builder is the best AS (and MXML) editor out there.

Aptana Studio (FREE). A one-stop show IDE for HTML, CSS, Javascript, Python, Ruby and PHP. It's Eclipse-based.

MAMP (free). An Apache server for the Mac.

RegExp Tool (free). This is the best app I've every found for testing out Regular Expressions.

DESIGN
Adobe CS4 Suite. Of course.

REMOTE FILE MANAGEMENT
Transmit. My FTP client of choice. It allows you to edit files while they're online.

Syncro SVN Client. Full-featured GUI client for the Mac.

Zumodrive (free for 1GB, $ for more). A virtual harddrive that you can access from anywhere.

Transmission. Solid bittorrent client.

SOCIALIZING
Colloquay (free). All-you-need IRC client.

Tweetie (free if you use add-supported version). Solid Twitter client for the Mac (and iPhone).

EMULATING
Virtual Box (free). Run Windows on the Mac. Like Parallels, but free.

READING
Vienna (free). RSS reader with a build-in Browser.

Tofu (free). Paste text into this cool tool, and it formats the text into narrow columns, which makes online reading much easier. It's the closest you'll get to a Kindle on your computer screen.

MISC
Eigenclock (free). Replacement for the system clock. Includes a dropdown calendar.


Caffeine
(free). Lets you temporarily disable sleep, so that your monitor doesn't power down. Useful if you're watching a long video.

Default Folder X. I seriously don't know how I lived without this. It wraps open, save and save-as dialogs inside a feature-packed window containing shortcuts to folders you need to access quickly. You can define any shortcuts you want.