My Secret Life as a Spaghetti Coder
home | about | contact | privacy statement
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!


Comments
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 - 5 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 - 5 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 - 5 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 - 5 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 - 5 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 - 5 hrs

Well, we may still be able to learn things from it...

Posted by Sam on Feb 17, 2007 at 10:35 AM UTC - 5 hrs

Leave a comment

Leave this field empty
Your Name
Email (not displayed, more info?)
Website

Comment:

Subcribe to this comment thread
Remember my details
Google
Web CodeOdor.com

Me
Picture of me

Topics
.NET (19)
AI/Machine Learning (14)
Answers To 100 Interview Questions (10)
Bioinformatics (2)
Business (1)
C and Cplusplus (6)
cfrails (22)
ColdFusion (78)
Customer Relations (15)
Databases (3)
DRY (18)
DSLs (11)
Future Tech (5)
Games (5)
Groovy/Grails (8)
Hardware (1)
IDEs (9)
Java (38)
JavaScript (4)
Linux (2)
Lisp (1)
Mac OS (4)
Management (15)
MediaServerX (1)
Miscellany (76)
OOAD (37)
Productivity (11)
Programming (168)
Programming Quotables (9)
Rails (31)
Ruby (67)
Save Your Job (58)
scriptaGulous (4)
Software Development Process (23)
TDD (41)
TDDing xorblog (6)
Tools (5)
Web Development (8)
Windows (1)
With (1)
YAGNI (10)

Resources
Agile Manifesto & Principles
Principles Of OOD
ColdFusion
CFUnit
Ruby
Ruby on Rails
JUnit



RSS 2.0: Full Post | Short Blurb
Subscribe by email:

Delivered by FeedBurner