At times, I like to push the limits of the languages I work in, just to see what I can learn. A while back,
I got the idea of using structs as objects in Coldfusion, since instantiating objects is so slow. Of course,
this would only be useful if you had tons of objects, and if it was substantially quicker than creating
real objects. Anyway, I decided to see what I could come up with.
Unfortunately, I don't think this would be any more efficient (and possibly less),
because I need to do three file operations. But, I thought I'd share what I came up with regardless.
Basically, what happens is that you tell pseudo_object.cfc what object you want to create, and it creates
a fake object that behaves like a real one. It reads the
CFC
you tell it you want an object of. It creates a struct, finds the name of the variable you stored it in
(multiple references won't matter, to my knowledge), rewrites the methods to use the scope+name of
that variable, plus an extra
this
or
variables
scope to make up for
variables named the same in different scopes within the CFC. Then, it attaches those methods to
the struct and you now have a struct which behaves like
an object. It won't work if you have any code outside of methods, however. Also, another problem may
arise if you have something like
this.variables
.
Since I'm not entirely sure that made any sense, I'll share the code now:
<cfcomponent name="pseudo_Object"
>
<cfset variables.varscope = ""
>
<cfset variables.obj_index = 0>
<cfset this.objects = arrayNew(1
)>
<cffunction name="po_init"
access="public"
output="false"
>
<cfargument name="varscope"
required="true"
>
<cfset variables.varscope = arguments.varscope>
<cfreturn this>
</cffunction>
<cffunction name="populateObject"
output="true"
access="public"
>
<cfargument name="relative_path_to_cfc"
required="true"
>
<cfargument name="object"
required="true"
hint = "Struct returned from prepareObject()"
>
<cfset var local = structNew()>
<cfset local.objName = getTheNameOfTheVariableThatContainsMe(arguments.object.pseudo_id)>
<cfset local.newfile = arguments.object.pseudo_id & ".cfm"
>
<cffile action="read"
file="#expandpath(arguments.relative_path_to_cfc)#"
variable="local.theCfc"
>
<cfset local.theCfc = replacenocase(local.theCfc, "variables."
, "#local.objName#.variables."
, "all"
)>
<cfset local.theCfc = replacenocase(local.theCfc, "this."
, "#local.objName#."
, "all"
)>
<cffile action="write"
file="#getdirectoryfrompath(getcurrenttemplatepath())##local.newfile#"
output="#local.theCfc#"
>
<cfinclude template="#local.newfile#"
>
<cfloop collection="#variables#"
item="local.key"
>
<cfif isCustomFunction(variables[local.key]) and not listfindnocase("po_init,getObject,prepareObject,getTheNameOfTheVariableThatContainsMe"
,local.key)>
<cfset this.objects[variables.obj_index][local.key] = variables[local.key]>
<cfset structdelete(variables, local.key)>
</cfif>
</cfloop>
<cffile action="delete"
file="#getdirectoryfrompath(getcurrenttemplatepath())##local.newfile#"
>
<cfreturn this.objects[variables.obj_index]>
</cffunction>
<cffunction name="prepareObject"
returntype="struct"
access="public"
>
<cfset variables.obj_index = variables.obj_index + 1>
<cfset this.objects[variables.obj_index] = structNew()>
<cfset this.objects[variables.obj_index].pseudo_id = createUUID()>
<cfreturn this.objects[variables.obj_index]>
</cffunction>
<cffunction name="getTheNameOfTheVariableThatContainsMe"
output="false"
access="private"
>
<cfargument name="keyToFind"
>
<cfset var local = structNew()>
<cfset local.result = ""
>
<cfset local.scopes = arrayNew(1
)>
<cfset local.scopes[1] = variables.varscope>
<cfset local.scopeNames[1] = "variables"
>
<cfset local.scopes[2] = session>
<cfset local.scopeNames[2] = "session"
>
<cfset local.scopes[3] = application>
<cfset local.scopeNames[3] = "application"
>
<cfloop from="1"
to="#arrayLen(local.scopes)#"
index="local.i"
>
<cfloop list="#structkeylist(local.scopes[local.i])#"
index="local.name"
>
<cfif isStruct(local.scopes[local.i][local.name]) and
structKeyExists(local.scopes[local.i][local.name],"pseudo_id"
) and
local.scopes[local.i][local.name].pseudo_id eq arguments.keyToFind>
<cfset local.result=local.scopeNames[local.i] & "."
& local.name>
<cfbreak>
</cfif>
</cfloop>
</cfloop>
<cfreturn local.result>
</cffunction>
</cfcomponent>
And so we have something to test with, I've made this simple test.cfc:
<cfcomponent name="test"
>
<cffunction name="init"
>
<cfreturn this>
</cffunction>
<cffunction name="setVar1"
>
<cfargument name="value"
>
<cfset variables.var1 = arguments.value>
</cffunction>
<cffunction name="getVar1"
>
<cfreturn variables.var1>
</cffunction>
<cffunction name="setThisVar1"
>
<cfargument name="value"
>
<cfset this.var1 = arguments.value>
</cffunction>
<cffunction name="getThisVar1"
>
<cfreturn this.var1>
</cffunction>
</cfcomponent>
And finally, here we see how to create a pseudo-object of test.cfc:
<!--- test_pseudo_object.cfm ---->
<cfset pseudoObjCreator = createobject("component"
, "pseudo_object"
).po_init(variables)>
<cfset session.pseudoObj = pseudoObjCreator.prepareObject()>
<cfset session.pseudoObj = pseudoObjCreator.populateObject("test.cfc"
, session.pseudoObj)>
<cfset session.pseudoObj.setVar1("five"
)>
<cfset session.pseudoObj.setThisVar1("this var 1"
)>
<cfset pseudoObj2 = pseudoObjCreator.prepareObject()>
<cfset pseudoObj2 = pseudoObjCreator.populateObject("test.cfc"
, pseudoObj2)>
<cfset pseudoObj2.setVar1("six"
)>
<cfoutput>
#session.pseudoObj.getVar1()#<br/>
#session.pseudoObj.getThisVar1()#<br/>
#pseudoObj2.getVar1()#
</cfoutput>
Now, it was fun to play around with this stuff, but I realize it's probably useless (I mean, I didn't do
any benchmark testing to see if it was faster than object creation, since I had to read and
write to the file system). Further, I'm not at all happy with the design. My main concern is that the
semantics are exposed: a user of this code needs to know they have to call the methods in the order shown
in test_pseudo_object.cfm to have it work. That could be fixed however, if I required that the name of one
of the variables (and its scope) be passed into the CFC via the
init()
method. Then,
it would return the struct, all in one go. In fact, if I were to ever start using this, I would reimplement
it in that fashion, and it wouldn't be very hard.
Anyway, what do you think of this useless ...
Hey! Why don't you make your life easier and subscribe to the full post
or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate
wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!
Leave a comment
http://cfobjects.sourceforge.net/this was done back in the CF4.0 days. I was really cool and useful at the time before cfcomponent came along. Sometimes I wish Macromedia would of had Ralph on the team when they decided to implement objects in CF.
Posted by
tony Petruzzi
on Jan 24, 2007 at 11:54 AM UTC - 6 hrs
Cool! I started using CF in version 4.5, and cfobjects sounds familiar to me, but I don't recall what it was! I read a little bit on that site. I notice a method to invoke methods... wonder how that worked? I guess I'll have to check it out more in depth later.
Of course, I don't recall cffunction being around until CF5 (not sure if it was out before then, but I don't recall knowing about it until that time), and I probably never used it until CF6. My memory can be hazy at times, especially that far back, so take that into consideration before relying on anything I say from way back when. =)
Also, it appears I'm slow to adopt new technology... or else I used to be. Now, I try to be at least a little more up to date... =)
Thanks Tony!
Posted by
Sam
on Jan 24, 2007 at 12:08 PM UTC - 6 hrs
Oh, I see! (I think). It used a different file for each function, since there was no cffunction, right? And the invoke method then invoked that template? Cool stuff.
Learn something new every day.
Posted by
Sam
on Jan 24, 2007 at 12:11 PM UTC - 6 hrs
CFObject although cool, never really caught on. For one thing, you had to have mapping placed on the server and at that time hosting companies were reluctant to do so. Also before cfcomponent many people didn't see a need for objects in web development.
Ralph was a really ingenious guys and I remember seeing his presentation on CFObject a couple of times at my CFUG. I tried to use it after that, but I guess my brain was cluttered with procedural programming stuff, because it didn't really start to make sense until I started using cfcomponents.
Posted by
Tony Petruzzi
on Jan 24, 2007 at 01:00 PM UTC - 6 hrs
Well, given what I remember, I can't imagine it would have been very "natural" to use either.
Posted by
Sam
on Jan 24, 2007 at 01:11 PM UTC - 6 hrs
Actually, CFObject was kind of inspiring when it first came out. I took it apart, then built a new one based on the file=method inheritance and polymorphic principles, did away with the registration and garbage collection, and added CRUD/business object functionality.
Went through about three versions refining it and adding features, used as the base for four or five production sites, and was about to release it to the community when MX6 and components appeared on the scene.
Posted by Michael Long
on Feb 17, 2007 at 04:26 AM UTC - 6 hrs
Well, we may still be able to learn things from it...
Posted by
Sam
on Feb 17, 2007 at 10:35 AM UTC - 6 hrs
Leave a comment