Posted by Brad Wood
Sep 20, 2008 11:12:00 UTC
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:
[code]<cfcomponent name="bean" hint="I am the base class for a bean"> <cffunction name="init" returntype="any"> <cfset structappend(variables,arguments)> <cfreturn this> </cffunction> <cffunction name="OnMissingMethod" access="public" returntype="any" output="false"> <cfargument name="MissingMethodName" type="string" required="true"> <cfargument name="MissingMethodArguments" type="struct" required="true"> <cfif left(arguments.MissingMethodName,4) eq "get_" and structkeyexists(variables,right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4))> <cfreturn variables[right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4)]> <cfelseif left(arguments.MissingMethodName,4) eq "set_" and structkeyexists(variables,right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4))> <cfset variables[right(arguments.MissingMethodName,len(arguments.MissingMethodName)-4)] = MissingMethodArguments> <cfelse> <cfthrow message="No Such Method Name" detail="A method named #arguments.MissingMethodName# was not found in this Component"> </cfif> </cffunction> </cfcomponent>[/code]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:
[code]<cfcomponent name="client_image" extends="bean" hint="I am an image of an e-mail in a specified client."> <cffunction name="init" returntype="any"> <cfreturn super.init(argumentcollection=arguments)> </cffunction> </cfcomponent> [/code]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:
[code]<cfset local.tmp_object = createobject("component","client_image").init(argumentcollection=local.this_client)>[/code]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?