So, a while ago, faced with the fact that CreateObject and would be disabled on my main development environment by zealous security requirements, I began searching for a way to still allow myself to develop and work with OOP, even though the "Community Best Practice" methods for working with Objects weren't allowed in my dev environment. After a bit of experimentation and practice I came up with a pattern that worked remarkably well for me, and actually solved some of the other problems that I noted people were having with the "standard" invocation pattern.
My pattern is reliant on two things:
- The CFC must have (or inherit) a public init function, which performs the duties of a traditional object "Constructor".
- The init function must return a reference to the object, generally by ending with.
In the traditional paradigm, invoking an object is (almost) always performed in two steps. First , you get a fresh new object with either the CFOBJECT tag, or the CreateObject function. This object's psuedo-constructor (any code outside the CFFunction tags) has been executed, but any intializing values (dsn for a DAO, the MachII.framework.AppManager for a MachII plugin, etc..) have not yet been fed into the object, so it's really "half baked". It's not ready for general consumption. In order to make it ready for general consumption, you have to call an initializer on it. By convention, this function is called "init", and accepts any arguments that are necessary to make the object "ready for primetime".
A few people began to notice that this pattern broke your code up into multiple lines, which could be accidentally separated, resulting in the unitialized object references being released into the wilds of your program. One of these people came up with the bright idea of returning a reference to "this" from the init function. This allows your init function to be chained to the CreateObject call like so:
<cfset newObject = createObject("component","foo").init() />
This still is possible to mess up, but as Joel Spolsky once pointed out, one of the keys to making your code stable is making wrong code look wrong. If all of your CreateObject statements are immediately chained to an init function, then it's easy to track down errors where your instantiated-but-unitialized objects are released into the wild. If you see a CreateObject anywhere that isn't immediately followed an init, then you know at least one thing you need to fix.
This development inspired me. I knew that CFINVOKE could be used to in one operation, instantiate an object, and call a function on that object. The return value of that function call could be saved. What would happen if I ran a CFINVOKE on an init function that returned a reference to "this"? It turns out that the result is exactly the same as the CreateObject chaining that people were recommending before, but it didn't require access to the "forbidden" functionality.