Friday, April 29, 2005

Practical aggregation in CFCs

Basic aggregation with cfcs is pretty straight forward. There are plenty of examples where you have a simple 1-to-1 relationship between a parent object and its child. For example, you may have created an "Address" property that contains a series of string properties (line1, line2, apt, city, state, zip, etc.).

Elsewhere, you might create a "Customer" object that has properties like customerNumber, name, phone that are strings. You also give it a property called mailingAddress that has a type of Address (defined by the address object created earlier).

But what happens when you have to relate more than one child instance to the parent?

Suppose we're working with a set of objects like the ones shown in the diagram. Here, things are starting to get a little more complicated. We now have a parent object(car) that needs to contain several instances of our wheel object. In order to handle this, we create a wheelArray property in Car. Each wheel that needs to be on our car can now occupy a position in our array.

Lets skip ahead here and presume that all of the classes have a full set of basic getters and setters as well as a constructor that returns the object. We've also created an instance of our car object called myCar and that all of the properties have been populated with default values.

We can now reference one of the wheels and set its air pressure by doing something like this:


wheelArray = myCar.getWheelArray();
wheelArray[1].setAirPressure(32);


Say we now need to tell our car that the 3rd lug nut on wheel 1 is a locking lug. We could take the pattern above a step further by doing this:


lugArray = wheelArray[1].getLugArray;
lugArray[3].setLocking(true);


We can continue down the chain working with setting temporary variables for a few levels of nested objects, but eventually we run into problems. I've run into problems where the referencing of objects seems to fall apart and executing setter methods doesn't change the value of the object. I think this is due to the way CF creates copies of complex objects. Deep levels only get copied by reference.

The way I've gotten around this issue is to create additional getters for properties that return arrays of objects. You require a single argument to reference a specific item in the array. In the example here, Car.cfc would get a new getter like this (note, I put all my properties into a structure called instance):


<cffunction name="getWheel" access="public" output="false" returntype="Wheel">
<cfargument name="arrayIndex" required="true" type="numeric" />
<cfreturn variables.instance.wheelArray[arguments.arrayIndex] />
</cffunction>


We can now set the air pressure of a specific wheel without assigning the array to a temporary variable.


myCar.getWheel(1).setAirPressure(32);

Setting the lug can be done in one line now, too.

myCar.getWheel(1).getLug(3).setLocking(true);


I think (but I'm not sure) that this works because no arrays are getting returned by the string of components and none are getting assigned to temporary variables. There is an unbroken chain of objects being returned until we get to the last method that does the actual work. Hopefully, somebody can verify that this is the case.

It took me a long time through trial and error to figure out a way to handle this (I have another method for dealing with this that involves recursive variable assignments, but I can pretty much guarantee that it will NEVER wind up as a best practice). For me, this is one of those things that seems so simple once you know about it, but is a real time waster until you do.

For me, it seems that "reading the books" only gets me so far. Inevitably putting the concepts into practice gets more complicated when I'm trying to build a real application.

Thanks to Brendan O'Hara for his article Design Patterns in ColdFusion: Composite Pattern for getting me started.

2 comments:

  1. hi mike, thanks for the MDG file for EA. Having just shifted from a .net world, I was feeling lost without it. Can you let me know if this file will also allow me reverse engineer code into EA and if so how.

    Apologies for using your blog to contact you but I couldn't find a link to you email address.

    thanks again. Pete (aka lad4bear)

    ReplyDelete
  2. Sorry to disappoint you Peter, but EA does not currently support round-tripping for ColdFusion. The makers of the product don't specifically support the language, they have just exposed some of their scripting technology that let made it possible to generate cfc shells from the model.

    I know that the EA team is looking at exposing some reverse engineering technology in a future release, I'm pretty sure that it will be difficult to make ColdFusion take advantage of it. Where other languages have strict coding structures, CF lets you pretty much do whatever you want. If there is any chance of doing this, I think it's going to take a combination of flexible code parsing tools in the product in addition to strong coding standards/best practices.

    We'll just have to keep our fingers crossed!

    ReplyDelete