Monday, September 22, 2008

Darren on Flex: Overloading the Constructor - Revisted

This is just a follow up to Overloading the Constructor to elaborate on my response to a comment made by Ben. I show how to:
  • Hide the public constructor by hiding the implementation class.
  • Expose the class via an interface.
  • Provide arbitrary factory methods in a factory class.

The Interface

//example\IPerson.as
package example {
    public interface IPerson {
        function get fullName():String
    }
}

The Factory and Implementation Class

//example\PersonFactory.as
package example {
    public class PersonFactory {  
        public function newPersonWithFirstName(firstName:String):Person {
            return new Person({firstName: firstName});
        }  
        
        public function newPersonWithLastName(lastName:String):Person {
            return new Person({lastName: lastName});
        }  
        
        public function newPersonWithFirstAndLastName(firstName:String, lastName:String):Person {
            return new Person({firstName: firstName, lastName: lastName});
        }  
        
        public function someDudeWithOnlyMiddleNames(... middleNames):Person {
            return new Person({middleNames: middleNames});
        }
        
        public function andTheFullShaBang(firstName:String, lastName:String, ... middleNames):Person {
            return new Person({firstName: firstName, middleNames: middleNames, lastName: lastName});
        }
    }
}

import example.IPerson;

class Person implements IPerson {
    private var firstName:String;
    private var lastName:String;
    private var middleNames:Array;
    
    public function get fullName():String {
        return firstName + (middleNames ? " " + middleNames.join(" ") + " ": " ") + lastName;
    }
    
    public function Person(params:Object) {
        for (var property:String in params) {
            this[property] = params[property];
        }
    }
}  

The FlexUnit Test (for good measure)

//example\PersonFactoryTest .as
package example {
    import flexunit.framework.TestCase;

    public class PersonFactoryTest extends TestCase {
        public function PersonFactoryTest(methodName:String=null) {
            super(methodName);
        }
        
        public function testCreatingBenjaminNortier():void {
            var factory:PersonFactory = new PersonFactory();
            
            var ben:IPerson = factory.newPersonWithFirstAndLastName("Benjamin", "Nortier");
            
            assertEquals("Benjamin Nortier", ben.fullName);
        }
        
        public function testCreating21stCenturyCodeWorks21ccwblogspotcomBenjaminNortierWith():void {
            var factory:PersonFactory = new PersonFactory();
            
            var ben:IPerson = factory.andTheFullShaBang("Benjamin", "Nortier", "21st Century Code Works", "21ccw.blogspot.com");
            
            assertEquals("Benjamin 21st Century Code Works 21ccw.blogspot.com Nortier", ben.fullName);
        }
    }
}
... and the results snapshot: Cheers

1 comment:

Benjamin Nortier said...

Nice. I quite like this bit:

public function Person(params:Object) {
for (var property:String in params) {
this[property] = params[property];
}
}

with the [] for properties. Always nice to see some native property support in languages, and some form of named parameters. Although I would argue that this:

(make-instance 'person :first-name "Ben" :last-name "Nortier")

is slightly more expressive than this:

new Person({firstName: "Ben", lastName: "Nortier"})

but I never said I was completely objective :) Seeing somethign like this:

new Widget(true);

always irritates me as I have no idea what the parameter means, requiring either looking at some code or documentation lookup. But if the language doesn't afford naming parameters easily, then this is what you'll end up with more often than not, at least for booleans...