Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with:

Exploring ColdFusion 9 Implicit (Synthesized) Getter And Setter Behavior

By Ben Nadel on
Tags: ColdFusion

Yesterday, there was some discussion as to whether the synthesized accessors (sometimes referred to as implicit getters and setters) in ColdFusion 9 were faster or more efficient than hand-coded getter and setter methods. As far as efficiency goes, I have to assume the difference would be negligible. But, the whole conversation got me thinking that I might not know enough about the very nature of ColdFusion 9's new implicit getters and setters.

As Fred pointed out yesterday, the synthesized getters and setters in ColdFusion 9 are not simply generated user defined functions (UDF). Rather, they are specialized extensions of the UDF Java class. Using some reflection (ie. getClass() and getSuperClass()), we can see that the class hierarchy for the implicit UDFs is as such:

  • coldfusion.runtime.ImplicitGetter
  • coldfusion.runtime.ImplicitUDFMethod
  • coldfusion.runtime.UDFMethod
  • coldfusion.filter.FusionFilter

Knowing this - that implicit getters and setters aren't just generated UDFs - I wanted to take some time to see how they behave in different situations. To power my exploration, I created a ColdFusion component that has one hard-coded method (init) and four synthesized methods:

Girl.cfc

  • <cfcomponent
  • output="false"
  • accessors="true"
  • hint="I represent a Girl domain entity.">
  •  
  •  
  • <cfproperty
  • name="name"
  • type="string"
  • validate="regex"
  • validateparams="{ pattern=.+ }"
  • hint="I am the name of the girl."
  • />
  •  
  • <cfproperty
  • name="age"
  • type="numeric"
  • validate="regex"
  • validateparams="{ pattern=(1[89]|[2-9]\d+|1\d\d) }"
  • hint="I am the age of the girl. I must be 18 years or older."
  • />
  •  
  •  
  • <cffunction
  • name="init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return an initialized component.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="name"
  • type="string"
  • required="true"
  • hint="I am the name of the girl."
  • />
  •  
  • <cfargument
  • name="age"
  • type="numeric"
  • required="true"
  • hint="I am the age of the girl."
  • />
  •  
  • <!--- Store default values. --->
  • <cfset this.setName( arguments.name ) />
  • <cfset this.setAge( arguments.age ) />
  •  
  • <!--- Return this object reference. --->
  • <cfreturn this />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, I have defined two properties here - name and age - that both lead to the generation of getter and setter methods.

These synthesized methods store and retrieve values in and from the ColdFusion component's Variables scope, respectively. To make sure that no additional storage logic is taking place, I also created another ColdFusion component that can store and retrieve arbitrary values from its own Variables scope.

Generic.cfc (A Generic Property Map)

  • <cfcomponent
  • output="false"
  • hint="I store generic private variables.">
  •  
  •  
  • <cffunction
  • name="get"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I get the given property from the private variables scope.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="name"
  • type="string"
  • required="true"
  • hint="I am the name of the property being gotten."
  • />
  •  
  • <!--- Return the property. --->
  • <cfreturn variables[ arguments.name ] />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="set"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I store the given value in the private variables scope.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="name"
  • type="string"
  • required="true"
  • hint="I am the name of the property being stored."
  • />
  •  
  • <cfargument
  • name="value"
  • type="any"
  • required="true"
  • hint="I am the value of the property being stored."
  • />
  •  
  • <!--- Store the property. --->
  • <cfset variables[ arguments.name ] = arguments.value />
  •  
  • <!--- Return this object reference for method chaining. --->
  • <cfreturn this />
  • </cffunction>
  •  
  •  
  • </cfcomponent>

As you can see, this component will store any name-value pair in its own variables scope. We will be using this to test how synthesized methods work when their references are copied from one ColdFusion component into another.

Ok, now that we have our testing components defined, let's put these synthesized, implicit getter and setter methods through the wringer.

  • <!---
  • Create an instance of our ColdFusion component with
  • synthesized accessors. The init() method has been coded
  • manually, but everything else has been generated by
  • ColdFusion at runtime.
  • --->
  • <cfset jennifer = new Girl( "Jennifer", 33 ) />
  •  
  • <!--- Output the result. --->
  • <cfdump
  • var="#jennifer#"
  • label="CFC With Synthesized Accessors"
  • />
  •  
  •  
  • <!---
  • Create an instance of our generic holder. This will hold
  • arbitrary values in our variables scope which is where the
  • synthesized accessors store their data.
  • --->
  • <cfset generic = new Generic() />
  •  
  • <!---
  • Store alternative name and age properties so that this can
  • walk like a girl and talk like a girl (ducktypeing... sort of).
  • --->
  • <cfset generic
  • .set( "name", "Anna" )
  • .set( "age", 35 )
  • />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <br />
  • <br />
  •  
  • <cfoutput>
  •  
  •  
  • <!--- Get references to the sythesized methods. --->
  • <cfset getAge = jennifer.getAge />
  • <cfset setAge = jennifer.setAge />
  • <cfset getName = jennifer.getName />
  • <cfset setName = jennifer.setName />
  •  
  •  
  •  
  • <!---
  • Now, let's call the synthesized getters on the original
  • component (that synthesized them).
  • --->
  • Original Call:<br />
  • ---------------------------------------------------<br />
  • Jennifer Name: #jennifer.getName()#<br />
  • Jennifer Age: #jennifer.getAge()#<br />
  • <br />
  •  
  •  
  •  
  • <!---
  • Now, let's call the synthesized getters on the page's
  • variables' scope (ie. let's call it outside the context
  • of any component).
  • --->
  • Free-Floating Call:<br />
  • ---------------------------------------------------<br />
  • Variables Name: #getName()#<br />
  • Variables Age: #getAge()#<br />
  • <br />
  •  
  •  
  •  
  • <!---
  • Now, call the setName / setAge on the free floating methods.
  • Since ColdFusion's page context is always a "Variables"
  • scope, this should think it's in a component context (hint:
  • they're all just Page contexts).
  • --->
  • <cfset setName( "Paige" ) />
  • <cfset setAge( 25 ) />
  •  
  • <!--- Now, call the free-floating GETS again. --->
  • Free-Floating Call (Post-SETTERS):<br />
  • ---------------------------------------------------<br />
  • Variables Name: #getName()#<br />
  • Variables Age: #getAge()#<br />
  • <br />
  •  
  •  
  •  
  • <!---
  • Now, let's attach the free-floating, sythensized accessor
  • references to the Generic instance we created above.
  • --->
  • <cfset generic.getName = getName />
  • <cfset generic.setName = setName />
  • <cfset generic.getAge = getAge />
  • <cfset generic.setAge = setAge />
  •  
  • <!--- Try to update the age. It was 35, make it 37. --->
  • <cfset generic.setAge( 37 ) />
  •  
  • <!---
  • And, let's try to call the synthesized accessors on the
  • newly-contextual generic instance.
  • --->
  • Generic-Context Call:<br />
  • ---------------------------------------------------<br />
  • Generic Name: #generic.getName()#<br />
  • Generic Age: #generic.getAge()#<br />
  • <br />
  •  
  •  
  •  
  • <!---
  • Now, let's create a fake context in an attempt to override
  • the access of the synthesized getter.
  • --->
  • <cfset fakeContext = {
  • name = "Joanna"
  • } />
  •  
  • <!---
  • Try to call the getName() on the original Girl instance,
  • but try various approaches to hi-jacking the variables
  • search path.
  • --->
  • Fake-Context Call:<br />
  • ---------------------------------------------------<br />
  • Fake Name:
  • #jennifer.getName(
  • name = fakeContext.name,
  • variables = fakeContext
  • )#
  • <br />
  •  
  •  
  • </cfoutput>

We'll break this code down in a moment; but for now, when we run this code, we get the following page output:

 
 
 
 
 
 
ColdFusion 9 synthesized, implicit getter and setter behavior examined in depth. 
 
 
 

In the "Original Call" test, we are just checking to make sure that the implicit getters and setters worked as expected. We instantiated our Girl.cfc instance with the initial values, "Jennifer" and "33" and those values were successfully retrieved with the implicit getters.

In ColdFusion, user defined function (UDF) references can be passed around freely. In the "Free-Floating Call" test, we are looking to see if the synthesized getters and setters can also be passed around by reference. It turns out that they can; however, when accessed, they return undefined values (since neither name nor age is defined in the page's Variables scope).

Once we have seen the undefined values in the page context, we then use the free-floating synthesized setters to store both a name and age value. Using the synthesized getters after this simply demonstrates that the free-floating getters and setters are working on the Variables scope within the page context.

Using the Generic.cfc instance, we then demonstrate that the exact same behavior works when the implicit accessors are copied from one ColdFusion component to another. Once attached to the Generic.cfc instance, both the getters and setters access and mutate the given instance's Variables scope respectively.

So far, nothing too special has happened. In the last test, however, we start to see some behavior that is unique to synthesized methods. As I have demonstrated before, changes in the ColdFusion 9 scope search allows component properties to be overridden with named-arguments. In the last test, I am attempting to override the point-of-access for the "name" property by passing in both an argument of name, "Name," and an argument of name, "Variables." This should cover all the bases; however, as you can see in the output, neither of these attempts worked. Synthesized accessors must be using an unusual form of variable reference internal to the function - one that doesn't simply reference the "Variables" scope.

We already knew that the property names inside our implicit getters and setters were hard-coded. Now, we know a tiny bit more. Most of the behavior we see here is very much in alignment with standard ColdFusion user defined functions. The only thing that leaves me curious is the inability to override the internal Variables scope.



Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

Ok, I know it's a small thing, but it bugs me when you do

<cfset this.setName( arguments.name ) />
<cfset this.setAge( arguments.age ) />

in your Girl.cfc. I see this again and again and it's (imho) bad practice. If you are calling a method defined in your CFC (or implictly defined) you should NOT make use of this. If you ever switch the function to be private your code will stop working.

Reply to this Comment

@Ben,

Please forgive that this is tangential, and therefore technically off-topic:

HTML5 described some new behaviors for JavaScript in the form of data-* attributes (a.k.a. "custom data attributes") and this.dataset. I discovered the new behaviors in e-mail conversations with Hixie (HTML5 editor Ian Hickson) while writing jQuery's .defineDatasets() plug-in (now made obsolete when they added data-* interface to .data()).

In the HTML5 spec, specifying data-arf="bowwow" on an HTML element defines this.dataset.arf = "bowwow". Got it. No problem. Piece o' cake. But did you also know that setting this.dataset.arf = "woof" implicitly sets the data-arf attribute to "woof"? In other words, there are implicit setters for this.dataset properties that do .setAttribute().

For a true beat-the-browsers-to-the-punch implementation of HTML5 custom data attributes, I would've had to define getters, setters and deleters!! (That's right, to do .removeAttribute()!)

This is why Hixie told me that a true implementation would be impossible. Forget the cross-browser mess of getters and setters. Nobody's even thought about deleters, let alone implemented them in any browser.

When you think about it, deleters might be useful if you tend to write IsDefined/StructKeyExists code a lot.

Again, I apologize to be talking about implicit getters and setters in JS, when your post was on implicit getters and setters in CF. I just wanted to bring up the novel idea of deleters. I don't even know how you would prefix such methods for parallelism with existing naming conventions. (Example, getYou, setYou and forgetYou? Apologies to Cee Lo.)

That's it. I'm done babbling.

Reply to this Comment

@Raymond,

I think it's a force of old habits. I also rarely ever make anything Private as a byproduct of the "this-scope" usage. I can't seem to get away from my scopes.

@WebManWalking,

I have seen versions of Javascript that use some sort of implicit setter/getter for assignment statements... I think. When I was poking around in Rhino (Java-based JS runtime), I think the jQuery team had created a DOM for it (since it executes outside the browser) and it had all sorts of implict getter/setters.

I wish I knew more about that kind of stuff. My Javascript syntax understanding is totally old-school. I do know that you can use the data-* attributes and jQuery will automatically put them into the .data() collection.

But beyond that, it's all news to me. I wonder how long it would take all the browsers to support the same syntax, not to mention the same APIs (canvas, fonts, video, etc.).

Reply to this Comment

@WebManWalking,

It looks pretty interesting. Although, I'm not crazy about the syntax. I think in Groovy, they will use the accessors if they are defined, and the direct value if not. Unless I'm totally off-base there, it seems like that kind of automatic wiring would be pretty cool.

Reply to this Comment

The most frustrating thing for me about using implicit setters and getters is that the 'default' attribute on the

  • <cfpoperty>

tag is not respected, and there is no default value written to the VARIABLES scope when the CFC is created.

See this post for a more extensive investigation: http://flydillonfly.wordpress.com/2010/11/23/cfproperty-i-ask-you-why-not-default-correctly/

I have tried to log this as a bug. I'd love to hear if I am missing something here?

Reply to this Comment

@raymond,

i have the same problem with the scoping and cf is driving me nuts with its "this" scope.
since cf is built on java in my head i always think of cfcs as a separate java classes (which they do become at some point in their life), but this analogy fails miserably at "this" scope.

(rant)
it is considered "best practice" to scope variables in cf (like form., url., arguments. etc.), so why the cfc components can not access their own private members through "this" scope.
i really do not see any reason (or better said a use-case) why the private members of a class can not be scoped.
(/rant)

Reply to this Comment

@Nelle

I agree with that inconsistency too. And I'll follow up with another of my own :)

[rant]
It's almost as frustrating as the way you have to scope everything as LOCAL or var in CFC functions lest they end up in the VARIABLES scope. It's actually backwards compared to lots of other languages where unscoped variables declared in a function are just local to that function. Instead CF does it 100% backwards and stick them in the global object scope (i.e. VARIABLES) scope. So we have a situation where a small typo makes an entire function non-thread safe, not to mention the extra typing of var and LOCAL all over functions.
[/rant]

Reply to this Comment

Does anybody know if it is possible to track if any of set methods of a component has been called (in other words, if a component's state has changed since it has been initiated)? I need this because I don't think there is sense to save an entity to a database, unless it has been changed. With explicit setters it wouldn't be a problem. I am just thinking that ColdFusion's ORM might know if an object's properties have been updated before saving it. No?

Reply to this Comment

@Ciaran,

I believe the CFProperty default stuff has been logged as a bug. It is a weird thing. Though, I believe the documentation states that it will not use the default value. It is probably a backwards compatibility issue with pre-CF9 days when CFProperty really had no practical value outside of web services.

@Nelle,

I agree with you on the use of "this". That is a major reason why I don't use private methods - ColdFusion makes a hard distinction between access to the public and private methods even from within the CFC itself.

@Kirill,

I believe you can assign a global event listener for the ORM-enabled objects; but, I don't know much about it. After instantiation, I suppose you could also decorate the object with another objects that can announce events when setters/getters are called. But, that would take some finagling.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.