Synthesized Objects
The other day I was writing a wrapper CFC to consume a web service, and return the results. Several of the web services' method returned arrays of structs and I was in the mood to experiment, so I decided to have my CFC present the data back as an array of components. I knew that there would really never be more than 20 or 30 objects coming back so the cost of object instantiation wouldn't be a big deal. There were 7 different "types" of objects coming back in the various method containing mostly strings and an occasional binary image. I didn't want to do all the typing so I decided to toy with Object Synthesization similar to what Peter Bell has been talking about.
I wanted to create each object with minimal code, and I wanted all the properties to be private (variables scope). I also wanted to be able to call a getter to retrieve the data. By the time I was finished, I ended up with a base class called bean.cfc which actually had everything in it:
2
3 <cffunction name="init" returntype="any">
4 <cfset structappend(variables,arguments)>
5 <cfreturn this>
6 </cffunction>
7
8 <cffunction name="OnMissingMethod" access="public" returntype="any" output="false">
9 <cfargument name="MissingMethodName" type="string" required="true">
10 <cfargument name="MissingMethodArguments" type="struct" required="true">
11
12 <cfif left(arguments.MissingMethodName,4) eq "get_" and structkeyexists(variables,right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4))>
13 <cfreturn variables[right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4)]>
14 <cfelseif left(arguments.MissingMethodName,4) eq "set_" and structkeyexists(variables,right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4))>
15 <cfset variables[right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4)] = MissingMethodArguments[1]>
16 <cfelse>
17 <cfthrow message="No Such Method Name" detail="A method named #arguments.MissingMethodName# was not found in this Component">
18 </cfif>
19 </cffunction>
20
21 </cfcomponent>
My init method simply takes the argument collection and shoves it into the variables scope. This basically assumes that ALL properties are private and all the arguments coming in map to a property of the same name.
Lastly, my onmissingmethod handles all getters and setters. Once again, this assumes they are all named "get_" or "set_" followed by the name of the property, and that all properties are stored in the variables scope and can be served up directly. A custom getter or setter could be defined to "override" the onmissingmethod, but I didn't need to in my case.
Next, since I felt bad about directly instantiating my bean.cfc since it felt like a sort of abstract class, I created 7 mostly empty CFCs for each object named appropriately. Here is one called client_image.cfc:
2
3 <cffunction name="init" returntype="any">
4 <cfreturn super.init(argumentcollection=arguments)>
5 </cffunction>
6
7 </cfcomponent>
You can see all it does is extend the base bean, and call the super's init passing along the argument collection.
I would create my objects like this:
All in all it was an interesting experiment, but I can't say I like it.
Firstly, there were no OO warm fuzzies washing over me. Instead it felt like I had just taken a struct and placed a CFC "coat" over it.
Secondly, if I was going through so much trouble NOT to write code, I might as well have skipped the child classes and directly instantiated my bean class since I didn't override anything.
Thirdly, my bean was pretty dumb. And by dumb I mean not smart. And by not smart I mean the classes didn't self-document anything, didn't validation anything, didn't require anything. They just sat there and accepted whatever you shoved into them and coughed it back up when you asked.
In the end I think I might as well have just returned the array of structs. On a related note, can anyone tell me if there is an official name for the pattern (or anti-pattern) of using the dynamic getter/setter and a dynamic init like that?

In addition to precluding the possibility of a custom setter, it doesn't enforce any validation. I like what you did in your blog using the properties. That is kind of cool.
One of my struggles with validation is that it seems most useful during development when you are likley to make coding errors, or when someone other than you is going to be using your components. Specifically a third party you cannot trust (such as a web service). It seems that a large number of people who write and use their own components are already aware of the requirements and validation would be more of an extra step-- hence CF 8's setting to turn off data type checking.
To me personally, speed and simple code is almost more important than incredibly verbose data checking and explicitly defined accessors. It more RAD at least...