Flex Object Composition Car Example
I read a post called “Getting a bunch of components that work together to know about each other” on “The Joy of Flex” blog by David Coletta and figured I would share how it settled in my brain. First off cohesion and coupling of classes through inheritance or composition are very fluid things. When modeling the real world you can describe the same things quite differently depending on your prespective and usage. Composition, David calls it “containment”, is a very basic part of Object Oriented development. I will use a Car for my example and try to example David’s thoughts as I understood them. Now my take on this might be off but thats the joy of software engineering.
When David calls composition containment he also uses the terms of “descendant” and “ancestor” loosly to mean the a component that is contained by another and the one containing components, respectively.
My first object in the Car example would be the Car object. It will contain the Engine, Tire, and SteeringWheel object, all ancestors of Car. Thus Car is a ancestor to Engine as the Engine is a descendant to the Car.
David talks about 4 rules that he has developed by, first is all descendants need to provide a property that the ancestor can set. The property provides the descendant with a reference back to the ancestor.
The second is that the descendants “ancestor” reference property be set in the CREATION_COMPLETE handler of the descendant. In looking at components in Flex 2 we have the “creationComplete” event or more properly for customized components the “createChildren” or “childrenCreated” functions. The later are protected methods of any component supporting the UIComponent and are for us in custom components.
The third is that you create an API of getters or public members in the top most ancestor (top-level) component for the descendants to communicate to each other. There are 2 interpertation that I come up with off the top of my head. Using the Car example the first would be that the Car class would look like this:
[code lang="javascript"]
package com.renaun.objects
{
public class Car
{
public var _engine:Engine;
public var _steeringWheel:SteeringWheel;
public var _tires:Array; // array of Tire objects
}
}
[/cc]
Here you would just get a reference to any descendant objects of the Car instance. In terms of encapsulation this is not good. You should do something more like this:
[code lang="javascript"]
package com.renaun.objects
{
public class Car
{
private var _engine:Engine;
private var _steeringWheel:SteeringWheel;
private var _tires:Array; // array of Tire objects
private var isFourWheelDrive:Boolean;
public function Car()
{
super();
_engine = new Engine();
_steeringWheel = new SteeringWheel();
_tires = new Array();
_tires.push( new Tire( "RearLeft" );
_tires.push( new Tire( "RearRight" );
_tires.push( new Tire( "FrontLeft" );
_tires.push( new Tire( "FrontRight" );
isFourWheelDrive = true;
}
// Functions to get indirect access to descendants
public function turnKey():void {
_engine.startEngine();
}
// Functions to get indirect access to descendants
public function putIntoDrive():void {
_engine.engageTranmission();
var numWheels:int = ( isFourWheelDrive ) ? 4 : 2;
for( var i:int; i < numWheels; i++ )
_tires[ i ].startTurning();
}
}
}
[/cc]
Now the putIntoDrive doesn't really just start turning wheels, but you could put other composition into the car so the Tires where part of the drive line, axles, etc... and have the composed parts have their on API that makes more sense.
The fourth rule is that any initialization code in a descendant that needs access the ancestor should be done in the setter of the descendants "ancestor" reference property. This makes sense as its the first place where you know you have a valid reference to the ancestor.
For the full code example of the 4 rules above click here.
You can view the source here.
Pingback: The Joy of Flex » Check out Renaun Erickson’s Object Composition example
Pingback: Renaun Erickson