Wednesday, August 12, 2009

keep logic out of constructors

I'm sometimes tempted to overstuff constructors:


public class AxeMurderer
{
private var _name : String;
private var _crazy : Boolean;

public function AxeMurderer( name : String, crazy : Boolean )
{
_name = name;
_crazy = crazy;

if ( _crazy )
{
for ( var i : int = 0; i < 10000; i++ )
trace( "all work and no play makes Jack a dull boy" );
}
}
}


The overstuffing is the for loop (and possibly the conditional). I clean things up a bit by refactoring to...


public class AxeMurderer
{
private var _name : String;
private var _crazy : Boolean;

public function AxeMurderer( name : String, crazy : Boolean )
{
_name = name;
_crazy = crazy;

if ( _crazy ) writeManifesto();
}

private function writeManfesto() : void
{
for ( var i : int = 0; i < 10000; i++ )
trace( "all work and no play makes Jack a dull boy" );
}
}


But this creates a problem when I write an interface for the class.

Let me pause to say that I try to write my interfaces first, before writing the classes that implement them. That way I'm coding TO an interface. However, in real life that doesn't always happen. Sometimes I can't work out an interface without first writing at least a dummy implementation. In such cases, I go back and write an interface later. At least that I way I have the interface for similar classes.

So, having written the AxeMurderer class, I decide to create an IMurderer interface -- one that has a slightly broader scope than just axe murderers:


public interface IMurderer
{
//Hmmmm....
}


What do I put inside the interface? Looking back at my AxeMurderer class, the only public method is the constructor. That's because I got tempted by a (anti?) pattern: make the constructor build the object by calling a bunch of private methods.

So the interface would be...


public interface IMurderer
{
function AxeMurderer( name : String, crazy : Boolean )
}


But that would force any class that implemented it to have an AxeMurderer method -- terribly inappropriate for a Poisoner or a Strangler!

So, assuming that all types of murders might write a manifesto, I'm going to change my interface to...


public interface IMurderer
{
function writeManifesto() : void;
}


(I'm using a short example to illustrate a point. A real-life IMurderer would insist on methods like Kill(), Spare(), Escape(), Surrender() and BlameItOnJunkFood().)

Now I'll rewrite AxeMurderer as follows:


public class AxeMurderer implements IMurderer
{
private var _name : String;
private var _crazy : Boolean;

public function AxeMurderer( name : String, crazy : Boolean )
{
_name = name;
_crazy = crazy;

if ( _crazy ) writeManifesto();
}

//NOTE: Method now public
public function writeManfesto() : void
{
for ( var i : int = 0; i < 10000; i++ )
trace( "all work and no play makes Jack a dull boy" );
}
}


Note that I'm still calling writeManifesto() in the constructor. That's fine. Some IMurderer classes may choose to automatically write a manifesto; others may choose to only write one if writeManifesto() is called by an external client.

In general, I try to use constructors just for getting necessary external data into the class. Class logic goes elsewhere.

No comments:

Post a Comment