Using ColdFusion Method Pointers Without Updating ColdFusion Component Methods

Posted October 12, 2006 at 6:02 PM by Ben Nadel

Tags: ColdFusion

The other day, I talked about swapping methods in and out of ColdFusion components at run time. I did this by altering the methods defined in the component object:

  • <!--- Add new method to component. --->
  • <cfset objComp.NewMethod = NewMethodPointer />

... and then was able to call the new method directly, as if it were (and it was) a method of the component itself:

  • <!--- Call new method. --->
  • <cfset objComp.NewMethod() />

This was pretty cool, and it turns out, this is the idea behind ColdFusion Mixins which I had only heard about but never really researched.

What's neat about this is that the method auto wires itself into the appropriate scopes. Meaning, if the method refers to the THIS or the VARIABLES scope, then those references automatically refer to the THIS and VARIABLES scope of the methods new parent scope (ie. the component itself). Nicely done ColdFusion!

To take this one step in another direction, I wanted to see what would happen if you treated methods as behaviors, not as children of the component. To test this, I have created my Stripper object:

  • <cfcomponent
  • displayname="Stripper"
  • output="false"
  • hint="I am a stripper and I am crazy hot!">
  •  
  • <!---
  • Run the pseudo constructor to set up default
  • data structures.
  • --->
  • <cfscript>
  •  
  • // Set up default public properties.
  • VARIABLES.Actions = ArrayNew( 1 );
  •  
  • // Set up clothing properties.
  • VARIABLES.IsWearingShirt = true;
  • VARIABLES.IsWearingPants = true;
  • VARIABLES.IsWearingBra = true;
  • VARIABLES.IsWearingPanties = true;
  •  
  • </cfscript>
  •  
  •  
  • <cffunction name="Init" access="public" returntype="Stripper" output="no"
  • hint="Returns an initialized Stripper instance.">
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction name="AddAction" access="public" returntype="void" output="false"
  • hint="Adds an action for the strip routine.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="Action" type="any" required="true" />
  •  
  • <!--- Add to the actions array. --->
  • <cfset ArrayAppend( VARIABLES.Actions, ARGUMENTS.Action ) />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction name="Strip" access="public" returntype="string" output="false"
  • hint="I run through the actions you tell me to and then return my status.">
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = StructNew() />
  •  
  • <!--- Loop over actions and perform. --->
  • <cfloop index="LOCAL.ActionIndex" from="1" to="#ArrayLen( VARIABLES.Actions )#" step="1">
  •  
  • <!--- Get method pointer. --->
  • <cfset LOCAL.Action = VARIABLES.Actions[ LOCAL.ActionIndex ] />
  •  
  • <!--- Perform action. --->
  • <cfset LOCAL.Action() />
  •  
  • </cfloop>
  •  
  •  
  • <!--- Get new line. --->
  • <cfset LOCAL.NewLine = "<br />" />
  •  
  • <!--- Build the string buffer for our repsonse. --->
  • <cfset LOCAL.Response = CreateObject( "java", "java.lang.StringBuffer" ).Init( "" ) />
  •  
  • <!--- Add the physical checks. --->
  • <cfset LOCAL.Response.Append(
  • "Wearing Shirt: " & YesNoFormat( VARIABLES.IsWearingShirt ) & LOCAL.NewLine
  • ) />
  •  
  • <cfset LOCAL.Response.Append(
  • "Wearing Pants: " & YesNoFormat( VARIABLES.IsWearingPants ) & LOCAL.NewLine
  • ) />
  •  
  • <cfset LOCAL.Response.Append(
  • "Wearing Bra: " & YesNoFormat( VARIABLES.IsWearingBra ) & LOCAL.NewLine
  • ) />
  •  
  • <cfset LOCAL.Response.Append(
  • "Wearing Panties: " & YesNoFormat( VARIABLES.IsWearingPanties ) & LOCAL.NewLine
  • ) />
  •  
  •  
  • <!--- Return the response. --->
  • <cfreturn LOCAL.Response.ToString() />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, the Stripper keeps track of what articles of clothing she is wearing via several VARIABLES-scoped values. There is also a private array, Actions, which will hold the action the Stripper must perform during her strip tease. We can add actions to this array at any time and then ask the stripper to strip. Once she does this, she returns the status of her clothing. To demonstrate this, let's create a stripper and ask her to strip, having not provided any actions:

  • <!--- Create the stripper. --->
  • <cfset Stripper = CreateObject(
  • "component",
  • "Stripper"
  • ).Init()
  • />
  •  
  • <!--- Tell stripper to strip. --->
  • #Stripper.Strip()#

This gives us the following output:

Wearing Shirt: Yes
Wearing Pants: Yes
Wearing Bra: Yes
Wearing Panties: Yes

Since no actions were assigned, no clothing was removed (oh well).

Now, let's create a few actions that we can send her way.

  • <cffunction
  • name="RemoveShirt"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I remove my shirt... that's kind of hot.">
  •  
  • <!--- Remove shirt. --->
  • <cfset VARIABLES.IsWearingShirt = false />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="RemovePants"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I remove my pants... now we're getting somewhere.">
  •  
  • <!--- Remove pants. --->
  • <cfset VARIABLES.IsWearingPants = false />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="RemoveBra"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I remove my bra... is it cold in here???">
  •  
  • <!--- Remove bra. --->
  • <cfset VARIABLES.IsWearingBra = false />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>

As you can see, all of these functions are referencing the VARIABLES scope. Called on their own, they will not work, as free-floating functions do NOT have VARIABLES scopes. We could set these as built-in methods of the Stripper component, but that is what we have already tested. Let's try adding some of these to the Stripper's Action array and then asking her to strip again:

  • <!--- Add stipper actions. --->
  • <cfset Stripper.AddAction( RemoveShirt ) />
  • <cfset Stripper.AddAction( RemovePants ) />
  • <cfset Stripper.AddAction( RemoveBra ) />
  •  
  • <!--- Tell stripper to strip. --->
  • #Stripper.Strip()#

This gives us the following output:

Wearing Shirt: No
Wearing Pants: No
Wearing Bra: No
Wearing Panties: Yes

Awesome! As you can see, even though the methods were not "part" of the component, the scopes transferred over exactly how we would want them to. And, the structure of the ColdFusion component was not changed at all. Let's take a close look at how these are called:

  • <!--- Loop over actions and perform. --->
  • <cfloop
  • index="LOCAL.ActionIndex"
  • from="1"
  • to="#ArrayLen( VARIABLES.Actions )#"
  • step="1">
  •  
  • <!--- Get method pointer. --->
  • <cfset LOCAL.Action = VARIABLES.Actions[ LOCAL.ActionIndex ] />
  •  
  • <!--- Perform action. --->
  • <cfset LOCAL.Action() />
  •  
  • </cfloop>

As you can see, the Strip method just loops over the actions array, get the method point, and then executes it. Pretty cool. ColdFusion, so dynamic, so freakin' cool!




Reader Comments

Mar 14, 2007 at 11:25 AM // reply »
120 Comments

Minor nit-pick: free-standing functions *do* have a VARIABLES scope - it refers to the page in which the function is called. A function's VARIABLES scope is dynamically bound in the context in which it is called which is why it binds to the CFC's VARIABLES scope after being injected into the CFC and called as part of object.method()...


Mar 14, 2007 at 11:28 AM // reply »
11,238 Comments

Good point. The "context sensitive" Variables binding is pretty cool. It's like the THIS scope in Javascript.


Apr 20, 2008 at 3:27 AM // reply »
9 Comments

Ben,

This has to be the most interesting way to learn CFC's I have seen so far ... I actually got ... excited

about the component ...

you know, the object ...

yeah.

E


Apr 20, 2008 at 1:29 PM // reply »
11,238 Comments

@Edward,

Glad you like. Just trying to help people learn ;)


Dec 17, 2008 at 11:59 PM // reply »
2 Comments

I know this is an old post, but I thought I'd write up a quick thank you to Ben as stumbling into this post via google saved a lot of f**king about.

As a side note, you can also structAppend( baseComponent, helperComponent) if you have a helper class that you need to attach and you can't/don't want to use extends.

Great when using object factories.


Dec 18, 2008 at 8:18 AM // reply »
11,238 Comments

@Gabriel,

Using StructAppend() with components! I have never even thought about that. Very cool idea.


Dec 18, 2008 at 7:46 PM // reply »
2 Comments

See the following scenario (code deliberately abbreviated);

Code:
<cfcomponent>
<cffunction name="init" returntype="myClass">
<cfscript>
// init params, etc
</cfscript>
</cffunction>
</cfcomponent>

On the calling page:
<cfset myClass=createObject('component','myClass').init() />
<cfset myClass.myMethod() />

Obviously that fails... Imagine that as per the example we have created myMethod elsewhere, we append it to myClass
<cfset myClass.myMethod = myMethod />

Now this works...
<cfset myClass.myMethod() />

However, if for any reason you need to use that method INSIDE the class?
<cfcomponent>
<cffunction name="init" returntype="myClass">
<cfscript>
// init params, etc
</cfscript>
</cffunction>

<cffunction name="myInbuiltFunction" returntype="void">
<cfset myMethod() />
</cffunction>
</cfcomponent>

<cfset myClass.myInbuiltFunction() />

Fails because CF can't find the mixed in function.

Why would you want to do this? I have helper functions that I didn't want to have to write into EVERY object of a certain type, I couldn't use extends due to multiple inheritance limitations, blah, blah, blah...

There seems to be 2 main solutions to this problem, which have been blogged about by Joe Rinehart (http://firemoss.net/post.cfm/ColdFusion-Mixins--For-now-Ill-CFInclude) and Sean Corfield (http://corfield.org/blog/index.cfm/do/blog.entry/entry/Mixins) amongst others... using cfinclude and mixins.

Both have pros and cons and I'm not going to bloat this comment any further by debating each, I just wanted to highlight this particular issue and provide links to the other posts incase anyone else stumbles over this thread in search of answers.

(And I'm in the mixin camp, if anyone cares to know.)


Mar 23, 2009 at 7:59 PM // reply »
1 Comments

For you a woman is nothing but an "object"... :)


Mar 23, 2009 at 8:01 PM // reply »
11,238 Comments

@Byron,

He he :)


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools