Jared Rypka-Hauer, Lead ColdFusion Developer, Minneapolis, MN

Proud Parents of SQLSurveyor and PayPalMX
Viewing By Entry / Main
December 14, 2005 - back to top
I've had opportunity to debate the reasons behind setting up object instances wtih the syntax
<cfset application.myCart = createObject("component","my.cfcs.cart").init()>
and I've never really come up with a good explanation for people. Some people are really resistant to using an atomic call like that to create their objects. Why? Well the reasons I've heard are:
  • It's more readable when it's in two lines.
  • It's more flexible if you can create the object then initialize it.
  • I don't use init anyway. I put everything in the pseudo-constructor.
  • CF is not Java, so treating it like Java is dumb.
  • Init? What's init?
The problem is that these points are all ill-informed or short-sighted. Today, in a message on the colderfusion.com User Group mailing list Sean Corfield gave a very sound rationale for using atomic init calls to create objects. The text of his comment was:

<cf_impart who="Sean" what="advice" why="good">


Note: atomic initialization (good):
<cfset application.cartmanager =
createObject("component","CartManager").init(args) />


Non-atomic initialization (bad):
<cfset application.cartmanager = createObject("component","CartManager") />
<cfset application.cartmanager.init(args) />


Why is the second form bad? Because it creates a cartmanager object in
 application scope that is not fully initialized and which can be used
 by the read operations. This is why you should always:

  1. have an init() method
  2. init() should <cfreturn this /> (and have the appropriate returntype=)
  3. call init() on every object
  4. call createObject().init() in one operation
</cf_impart>

So you see, creating an object without initializing it in the same step is simply extra code at best and potentially dangerous at worst. Calling init() on an object in the same line as the createObject() call both streamlines the creation of the instance and makes sure that nothing can happen between the time the object is read into memory and when its dependencies are set into its instance data.

It's safer and easier, simpler and more straightforward... and in response to the bullet points way up there ^ I have to say this:
  • "More readable" is nice, but not in exchange for "less safe and effective." Perhaps a performance hit in exchange for readability is reasonable (sometimes), but writing unsafe code is never a good idea.
  • "More flexible" in this case is possibly due more to a lack of understanding or creativity than to a real need for flexiibility. If you need to use a two-stage setup for your object you should consider moving your setup code to a point before the createObect() call and pass in a pre-fabricated struct. There are times when performance can take a hit for being flexible, but stable, safe code is always more important. Then again, there are times when thread-safe isn't an issue... just be sure to keep it in mind.
  • Pseudo-constructors can't take arguments, instantly increasing the chances for thread-unsafe conditions and coupling. The best way to create flexible, extensible, safe designs is to parameterize (custom tags, UDFs and CFCs all benefit from this). Using the pseudo-constructor also executes code after the createObject() call and before init(), so while it may be an attractive place to do some things it can put extra loads on the server. In general? It's just not the best plan.
  • CF may not be Java, but the principles of OO and software engineering are constants across all platforms. There may be implementation differences between platforms (like CFMX treating functions as struct keys for Behavior Injection or Java's requirements for strong typing), but the concepts around which those rules are wrapped are consistent.
  • Init() is an ad-hoc convention that signifies a standard initialization method that code uses to pass data into a new instance of a CFC. In any community of developers certain conventions start to take shape just as a natural by-product of communication and usage... and it holds true in the CF community as well. Some method names have taken on standard meanings, like:
    • init() - used to pass data into a new CFC instance
    • configure() - used in some places where init() is controlled by another object (as in the case of some frameworks) and there's a need for a user to pass in custom startup data. In a case like this, init() is a method that can only be called by the framework with specific inputs. The configure() method, however, can be overridden to allow for user-defined inputs and the input data can be defined elsewhere (like an XML file).
    • dump() - returns a CFC's instance data in some sort of viewable/printable format (cfwddx or cfdump) for troubleshooting.
    • setInstance()/setMemento() & getInstance()/getMemento() - generally used to load a whole struct into a CFC's instance data at once, or to read it all out at once. The get methods are useful for troubleshooting and (once in a great while, like in a DAO read() method that returns a bean) setting up a whole instance at once. Use carefully, but it can be handy.
And just for Spike... for any attempt to apply a rigid set of rules to a large group, the Law of Averages will produce at least one reasonable and legitimate exception if not more. So while these "rules" are by far the best of practices and most always recommendable... you may actually break one or two for a good reason at some point.

I know I'm still waiting to break them, I just haven't found a good enough reason to yet.

Laterz!

Comments

Init? What's init?

:)


"First rule of programming, break the previous rule"





Aura skin for Raymond Camden's BlogCFC provided by Joe Rinehart.