Friday, November 13, 2009

rethinking constructors -- or what I learned from objective-c

Like many Flash developers, I've been studying Objective-C in a desperate attempt to ride the iPhone app train. (Though I think it left the station while I was still putting a token in the turnstile.)

One thing I like about Objective-C is its version of constructors, which usually start with the word "init." For instance, a method might be called just init(), or, if it takes arguments, it might be called initWithColor( 0xFF000 ) or initWithColorAndType( 0xFF0000, "large" );

Note: objective-C's syntax is different from Actionscript's, so in reality, the last method call above would look like this: [myObject initWithColor: 0xFF0000 andType: @"large"]; Since this post is about Actionscript, I'll keep everything in AS syntax.

You can, in a sense, overload Objective-C constructors. A class can't have two init() methods. But it can have init(), initWithColor() and initWithColorAndType(). You call the appropriate one, depending on how you want to initialize the object.

I also like the fact that every Objective-C class has a formal interface (defined in its header file). I do that for my Actionscript classes, too.

All of my classes (except DTOs and utilities with static members) implement at least one interface. Often, several classes will share the same interface. But even if a class is a loner, I make an interface file for it. I like this this clean separation between interface and implementation. When you browse my code, you can learn how everything fits together just by reading the interface files.

But you can't define constructors in an interface! That bothers me, because a constructor is -- or can be -- a way of communicating with a class. And if something is part of the way one communicates with a class, it is, by definition, part of that class's interface.

(I can't use my TV until I first turn it on. So the power button is kind of like a constructor. But it would be insane to say that the power button is not part of the TV's interface.)

Traditionally, interface files are only used when they are meant to be shared by multiple classes. In that sense, I get why they can't include constructors. Class A and Class B can't share a constructor, because Class A's constructor must be named A() and Class B's must be named B().

But since I make separate interface files for each class, I'd like to list ALL class inputs and outputs in those interface files.

Here's my solution: I never pass ANYTHING into a constructor. In other words, I would never write a constructor like this:

public function Person( name : String ) { ... }

Instead, person's constructor will look like this:

public function Person() { ... }

I don't care that the Person() method can't be listed in an interface file. It's not used as an interface element. It doesn't suck anything in from the outside world.

That's not to say I won't write a constructor function. I will if the class has any internal housekeeping it needs to do at start-up:

public function Person()
{
_kids = new Array();
}

So how do I specify that this a Person is named "George"?

var dad : Person = new Person();
dad.initWithName( "George" );

If it's also legal to have a nameless person, I would write this:

var dad : Person = new Person();
person.init();

All my classes have at least an init() method, even if it is just a placeholder husk. Want to know if my classes can be initialized any other way? Just check their interfaces:

public class Person implements ILifeform...

public interface ILifeform
{
function init() : void;
function initWithName( name : String ) : void;
function initWithNameAndGender( name : String, isMale : Boolean );
function eat() : void;
function die() : void;
function die() : void;
function addKid( name : String ) : void;
}

One objection to my system might be that initializer-method names could get absurdly long:

initWithWarpDriveShieldsPhotonTorpedosHoloDecksAndTransporters( ... );

To me, that's not an objection -- it's a warning! Even without my system, I would never write code like this:

var myShip : SpaceShip = new SpaceShip( true, false, true, true, false );

The more arguments a method takes in, the harder the code is for me to read, and the more apt I am to make a mistake. I have a loose rule of thumb that methods should take in no more than three arguments (and three is really pushing it).

So I'd change the above long init method to this:

initWithSpecs( specs : SpaceShipSpecs );

And I'd back this up with DTO class called ShipSpecs that had properties like warpDrive, sheilds, etc.

three little tricks

Here are a couple of tricks I learned form this article:

I try never to access an array's length in a for loop:

for ( var i : int = 0; i < people.length; i++ ) ...

That's bad, because each time the loop iterates, it has to dig into people and access its length property. I get a boost in optimization (without losing clarity) by doing this:

var numPeople : int = people.length;
for ( var i : int = 0; i < numPeople; i++ ) ...

But I'm bothered by the fact that both i and numPeople are throw-away variables (just needed for loop mechanics), yet only one is declared outside the for statement. So here's the trick:

for ( var i : int = 0, numPeople = people.length; i < numPeople; i++ ) ...

I advocate either that or this:

var i : int = 0;
var numPeople : int = people.length;
for ( i; i < numPeople; i++ ) ...

Note: you could also write the for loop this way:

for ( ; i < numPeople ; i++ )...

Trick two:

Let's say you need to loop backwards through an array by index. You could do this:

var arr : Array = [ 10, 20, 30, 40, 50 ];
for ( var i : int = arr.length - 1; i <= 0; i-- )
{
trace( arr[ i ] );
}

//output: 50, 40, 30, 20, 10

or (here's the trick) you could just do this:

var arr : Array = [ 10, 20, 30, 40, 50 ];
var i : int = arr.length - 1;
while( i-- )
{
trace( arr[ i ] );
}

//output: 50, 40, 30, 20, 10

Why does that work? Won't i keep decrementing past zero (-1, -2, -3...)? No, because in the Boolean sense, zero reads as false (all other numbers read as true). So while( 0 ) will stop the loop.

One final trick:

I didn't learn this one from the article I liked to above. I've seen it in various places.

Let's say you want to either set a variable equal to a value read in from an xml file OR, if there is no value in the xml file, you want to set the variable to some default value. (Hint: note the word "or" in the previous sentence.)

Here's what I used to do:

if ( valueFromFile == null ) speed = valueFromFile;
else speed = 10;

or I might have done this:

speed = ( valueFromFile == null ) ? valueFromFile : 10;

or even this:

speed = ( valueFromFile ) ? valueFromFile : 10;

What I hate about all those versions is the stuttering of "valueFromFile." Via the trick, I can eliminate it like so:

speed = valueFromFile || 10;

I love how clean this looks, especially when you're using it with multiple values:

var speed : int = speedFromFile || 10;
var color : uint = colorFromFike || 0xFF00FF;
var name : String = nameFromFile || "George";

getters and setters: no more faux properties

I am obsessed with making my code as easy to use as possible. One way I do that is to stay consistent whenever I can. If class A's kill() method does the same thing as class B's destroy() method, they shouldn't have two different names. They should both be kill() or destroy().

Adobe is terribly inconsistent (probably due to too many developers working without tight supervision) when deciding if properties should be accessed via methods or faux properties (getters/setters). Is it length or length(). Depends: arrays have length; XMLLists have length(). That always trips me up.

Accessors can present themselves as either properties or methods. For consistency, I've decided to use only one of these. It doesn't make sense to forgo methods, since I have to use methods for other purposes. So I'm forgoing properties.

No more "public function get length()" for me. From now on, I'll use getLength() and setLength(). For Booleans, I'll use isCold() and hasChildren(). Anyone using my classes -- including me -- will no longer have to look up a member to see if it's accessed as a property or method. It will always be a method.

Of course, another clean way of working is to be very rigid about whether a member is something a class has or something a class can do. A class might HAVE length but it can't DO length. I like this distinction in theory, but I'd rather not have to think about it in practice -- especially since some cases are ambiguous: does a book flipTo() page 18 or is its currentPage page 18?

Friday, November 6, 2009

Why You Should Override Clone In Event Subclasses

When I first learned how to code custom events in Actionscript 3.0, a friend told to override clone() when I subclassed flash.events.Event, but I didn't really understand why. So I ignored his advice.

It didn't seem to matter. I never wrote my own version of clone() and yet I never ran into any problems. But my friend's advice nagged at me. So I asked him why I should override clone(). He explained that if I didn't, clone() would return the standard Event type rather than my custom event type.

Let's say I create an Event subclass called KissEvent:


public calls KissEvent extends Event
{
public var withTongue : Boolean = true;

public static const BIG_SMOOCH : String = "bigSmooch";

...
}


As usual with events, I can set up a listener for KissEvent:

this.addEventListener( KissEvent.BIG_SMOOCH, onKiss );


function onKiss( kissEvent : KissEvent ) : void
{
...
}


The clone() problem occurs if I try to copy the Event via the clone() method it inherited from flash.events.Event:


function onKiss( kissEvent : KissEvent ) : void
{
var kissEventClone = kissEvent.clone();

trace( kissEvent.withTongue );
trace( kissEventClone.widthTongue );
}


The first trace, above, will output true. The second will cause an error. Flash will tell me that Event has no such property as withTongue, and in fact it doesn't. Sure, KissEvent has such a property, but clone() doesn't spit out KissEvents; it spits out Events.

public function clone() : Event

And you can't fool it this way:

trace( ( kissEventClone as KissEvent ).widthTongue );

Flash won't allow you to cast the clone as a KissEvent, because KissEvents MUST have withTongue properties. clone() spits out Events, which don't have withTongue properties, so there's no way Flash can convert an Event to a KissEvent. My variable name, kissEventClone, is misleading, because it really just holds an Event, not a kissEvent.

Once my friend explained this to me, I understood the problem, but I had a hard time caring about it. How often do I need to clone an event object? So far in my AS-coding experience, the answer is "never." The problem seems more theoretical than nuts-and-bolts actual.

But a year later, I encountered the REAL problem: double dispatches.

Let me explain: sometimes I create systems in which a listener forwards an event object to another listener. The first listener is a sort of middle-man between the dispatcher and the second listener. The middle man is both a listened and a dispatcher. It listens to the event, and, when it receives it, it dispatches the event to a second listener.

For example, say a game character has to listen for bullets coming at him. If a bullet hits him, he stops the bullet from flying any further and dies. That's the case UNLESS the character is a ghost. If a ghost gets hit by a bullet, it passes right trough him, so he just passes the event on to anything else that might be hit by it. Ghosts, like all characters, listen to Guns. Walls listen to Ghosts, because a Ghost might dispatch a bullet that passed through it, and that bullet might lodge in a wall.


event event
gun (dispatcher) --------------> ghost (dispatcher) ------------> back wall


Trouble is, a listener can't simply pass on an event. It can only pass on a cloned copy of the event. (That's a built-in "feature" of Flash's event system).


event event-clone
gun (dispatcher) --------------> ghost (dispatcher) -------------> back wall


Let's say that BulletEvents have a custom property called speed. Since clone() outputs Events (not BulletEvents), speed will be passed from gun to ghost but not from ghost to back wall.


event (speed=3) clone
gun (dispatcher) ---------------> ghost (dispatcher) -----------> back wall


So if the back wall tries to access speed, it's not going to find anything. Finally understanding the REAL problem, I made sure to override clone() in all my custom events. Here's a toy version of the solution:


public class MyEvent extends Event
{
public var myName : String;

public static const SOMETHING_HAPPENED : String = "somethingHappened";

public function MyEvent( type : String, myName : String,
bubbles : Boolean = true,
cancelable : Boolean = false )
{
this.myName = myName;
super( type, bubbles, cancelable );
}

override public function clone() : Event
{
return new MyEvent( super.type, myName, super.bubbles, super.cancelable );
}
}


Notice the overridden clone method. It returns a new MyEvent instance containing all the properties of the current instance. It truly IS a clone!

The one unfortunate thing is that clone() can't return a MyEvent. It must return an Event. That's because the clone() method in the superclass returns Event, and when you override a superclass's method, your new version must return the same type of object. But the returned Event is created by this statement: new MyEvent( super.type, myName, super.bubbles, super.cancelable ), so it DOES contain the myName property. This means we'll be able to use casting to get at myName, as I'll show below.

Now that we have our event, I'll show you the dispatcher/listen chain I have in mind:


event( w/myName ) clone( w/myname)
MyEventDispatcher ---------------> MyEventGrabbler (dispatcher) ------------> Main


Here's the code for MyEventDispatcher:


public class MyEventDispatcher extends EventDispatcher
{
public function dispatch() : void
{
var myEvent : MyEvent = new MyEvent( MyEvent.SOMETHING_HAPPENED, "Fred" );
dispatchEvent( myEvent );
}
}


Here's the middle-man code for MyEventGrabber:


public class MyEventGrabber extends EventDispatcher
{
private var _myEventDispatcher : MyEventDispatcher;

public function MyEventGrabber()
{
_myEventDispatcher = new MyEventDispatcher();
_myEventDispatcher.addEventListener( MyEvent.SOMETHING_HAPPENED, myHandler );
}

public function startTheEventBallRolling() : void
{
_myEventDispatcher.dispatch();
}

public function myHandler( myEvent : MyEvent ) : void
{
dispatchEvent( myEvent );
}
}


You might have expected the myHandler method to look like this:


public function myHandler( myEvent : MyEvent ) : void
{
dispatchEvent( myEvent.clone() );
}


While that's perfectly legal, it's not necessary. (Though maybe it's a good idea, as it makes what's happening explicit). When you re-dispatch an event, Flash uses clone() automatically.

Finally, here's the section of the Main class that catches the clone and accesses the myName property:


var myEventGrabber : MyEventGrabber = new MyEventGrabber();
myEventGrabber.addEventListener( MyEvent.SOMETHING_HAPPENED, myHandler );
myEventGrabber.startTheEventBallRolling();

function myHandler( event : Event ) : void
{
trace( (event as MyEvent).myName ); //Fred
}


Note that I use casting here to make Flash understand that the Event is actually a MyEvent. This casting only works because I overrode the clone() method in MyEvent.

There's one more potential problem that, admittedly, is less serious than the clone() problem. If I replace the above trace with this one...

trace( event );

... I won't see the myName property listed. Instead, I'll see this:

[Event type="somethingHappened" bubbles=true cancelable=false eventPhase=2]

The simple fix is to also override the toString() property in MyEvent, since when you hand trace() an object, it uses that object's toString() method. The simplest override is this:


override public function toString() : String
{
return "myName=" + myName;
}


But that will just trace out "myName=Fred", whereas what I want is...

[Event type="somethingHappened" bubbles=true cancelable=false eventPhase=2 myName="Fred"]

So I need to append the extra info to the end of the superclass's version of to string:


override public function toString() : String
{
return super.toString + " myName=" + myName;
}


Which gives me this:

[Event type="somethingHappened" bubbles=true cancelable=false eventPhase=2] myName=Fred

Because I'm anal, I need to get the myName=Fred inside the brackets. I also need to put quotation marks around "Fred," because it's a String. That's all easy to accomplish with a tiny bit of String manipulation. Here's my final version of MyEvent, with a robust toString() method. Note that I have also made a getter for myName, which stops random processes from being able to set myName to values that didn't come from MyEventDispatcher:


public class MyEvent extends Event
{
private var _myName : String;

public static const SOMETHING_HAPPENED : String = "somethingHappened";

public function MyEvent( type : String, myName : String,
bubbles : Boolean = true,
cancelable : Boolean = false )
{
_myName = myName;
super( type, bubbles, cancelable );
}

public function get myName() : String
{
return _myName;
}

override public function clone() : Event
{
return new MyEvent( super.type, _myName, super.bubbles, super.cancelable );
}

override public function toString() : String
{
var superString = super.toString();
var newProperties : String = ' myName="' + _myName + '"]';

//removes the closing bracket symbol from the end
//of the event-property list;
superString = superString.toString().substr( 0, superString.length - 1 );

return superString + newProperties;
}

}


The extra two methods, clone() and toString() add a lot of typing to the previously quick job of creating custom events. So I recommend that you use whatever templating system your IDE supports. If you're able to paste in generalized version of those method, it only takes a few seconds to make the necessary changes to support your particular event.