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() 2013 (Bloomington, MN) with:

Extending ColdFusion Components And Its Impact On Page Performance

By Ben Nadel on
Tags: ColdFusion

It was lunch time and I was curious as to how extending ColdFusion Components affects page processing time and performance. For those of you who don't know, you can "inherit" the functionality of one component by extending it and overriding functionality. Component instantiation has some overhead in ColdFusion so that leads me to think that using extend has even more overhead... but does it.

To test, I created a Girl.cfc and FullGirl.cfc. The Girl.cfc extends Person.cfc which extends AbstractBaseComponent.cfc. The FullGirl.cfc, on the other hand is "God Object" that can do everything that Girl.cfc does, except that it doesn't need to extend anything.

I then ran tests instantiation X number of components of each type:

  • <!--- Set the test size. --->
  • <cfset intSize = 1000 />
  •  
  • <!--- Create an array for the girls. --->
  • <cfset arrGirls = ArrayNew( 1 ) />
  •  
  • <!--- Resize the array. --->
  • <cfset ArrayResize( arrGirls, intSize ) />
  •  
  •  
  • <!---
  • Test the performance of components that
  • extend other components.
  • --->
  • <cftimer label="Extend Methodology" type="outline">
  •  
  • <!--- Loop over the size and add a girl. --->
  • <cfloop index="intIndex" from="1" to="#intSize#" step="1">
  •  
  • <cfset arrGirls[ intIndex ] = CreateObject(
  • "component",
  • "Girl"
  • ).Init(
  • FirstName = "Yuu",
  • LastName = "Sekine"
  • ) />
  •  
  • </cfloop>
  •  
  • </cftimer>
  •  
  •  
  • <!---
  • Test the performance of the a GOD component that
  • doesn't need to extend anything.
  • --->
  • <cftimer label="God Object Methodology" type="outline">
  •  
  • <!--- Loop over the size and add a girl. --->
  • <cfloop index="intIndex" from="1" to="#intSize#" step="1">
  •  
  • <cfset arrGirls[ intIndex ] = CreateObject(
  • "component",
  • "FullGirl"
  • ).Init(
  • FirstName = "Yuu",
  • LastName = "Sekine"
  • ) />
  •  
  • </cfloop>
  •  
  • </cftimer>

After a few tests of creating 1,000 instances, these are the results that I got:

Using Extended Components

1: 15,937 ms
2: 16,312 ms
3: 16,312 ms
4: 16,312 ms
5: 16,281 ms
6: 15,765 ms
7: 16,171 ms
8: 15,765 ms

Using GOD Components

1: 16,328 ms
2: 15,718 ms
3: 15,718 ms
4: 15,750 ms
5: 15,827 ms
6: 15,765 ms
7: 16,281 ms
8: 16,296 ms

As you can see, over a 1,000 object instantiations, the GOD object creation was only marginally faster. In fact, the GOD object creation was occassionally slower. I would say that this is a totally negligible difference. This is very good to know. Extending components makes code easier to extend and maintain. It's nice to know this can be done without really sacrificing any performance.

If you are interested in seeing the ColdFusion components that I wrote for this, they are listed below.

AbstractBaseComponent.cfc ColdFusion Component

  • <cfcomponent
  • displayname="AbstractBaseComponent"
  • output="false"
  • hint="Handles base functionality for CFCs.">
  •  
  •  
  • <!--- Set unique ID for this instance. --->
  • <cfset VARIABLES.InstanceID = CreateUUID() />
  •  
  •  
  • <cffunction name="Init" access="public" returntype="AbstractBaseComponent" output="false"
  • hint="Returns an initialized AbstractBaseComponent instance.">
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction name="EqualTo" access="public" returntype="boolean" output="false"
  • hint="Determines if this ">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="Comparable" type="any" required="true" />
  •  
  • <!--- Try to compare this object instance to the passed in. --->
  • <cftry>
  •  
  • <!--- Compare this ID to that ID. --->
  • <cfreturn NOT Compare(
  • VARIABLES.InstanceID,
  • ARGUMENTS.Comparable.GetInstanceID()
  • ) />
  •  
  • <cfcatch>
  •  
  • <!--- An error occurred, return false. --->
  • <cfreturn false />
  •  
  • </cfcatch>
  • </cftry>
  • </cffunction>
  •  
  •  
  • <cffunction name="GetInstanceID" access="public" returntype="string" output="false"
  • hint="Returns the instance's unique ID.">
  •  
  • <cfreturn VARIABLES.InstanceID />
  • </cffunction>
  •  
  • </cfcomponent>

Person.cfc ColdFusion Component

  • <cfcomponent
  • displayname="Person"
  • extends="AbstractBaseComponent"
  • output="false"
  • hint="Handles base functionality for the 'person' package of objects.">
  •  
  •  
  • <!--- Set default properties. --->
  • <cfset VARIABLES.Instance = StructNew() />
  • <cfset VARIABLES.Instance.FirstName = "" />
  • <cfset VARIABLES.Instance.LastName = "" />
  •  
  •  
  • <cffunction name="Init" access="public" returntype="Person" output="false"
  • hint="Returns an initialized Person instance.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="FirstName" type="string" required="false" default="" />
  • <cfargument name="LastName" type="string" required="false" default="" />
  •  
  • <!--- Store arguments. --->
  • <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
  • <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction name="GetFirstName" access="public" returntype="string" output="false"
  • hint="Returns first name.">
  •  
  • <cfreturn VARIABLES.Instance.FirstName />
  • </cffunction>
  •  
  •  
  • <cffunction name="GetLastName" access="public" returntype="string" output="false"
  • hint="Returns last name.">
  •  
  • <cfreturn VARIABLES.Instance.LastName />
  • </cffunction>
  •  
  •  
  • <cffunction name="SetFirstName" access="public" returntype="void" output="false"
  • hint="Sets first name.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="FirstName" type="string" required="true" />
  •  
  • <!--- Set value. --->
  • <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction name="SetLastName" access="public" returntype="void" output="false"
  • hint="Sets last name.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="LastName" type="string" required="true" />
  •  
  • <!--- Set value. --->
  • <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

Girl.cfc ColdFusion Component

  • <cfcomponent
  • displayname="Girl"
  • extends="Person"
  • output="false"
  • hint="Handles a Girl object.">
  •  
  •  
  • <!--- Set default properties. --->
  • <cfset VARIABLES.Instance.Gender = "Female" />
  •  
  •  
  • <cffunction name="GetGender" access="public" returntype="string" output="false"
  • hint="Returns gender.">
  •  
  • <cfreturn VARIABLES.Instance.Gender />
  • </cffunction>
  •  
  •  
  • <cffunction name="SetGender" access="public" returntype="void" output="false"
  • hint="Sets gender.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="Gender" type="string" required="true" />
  •  
  • <!--- Set value. --->
  • <cfset VARIABLES.Instance.Gender = ARGUMENTS.Gender />
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

FullGirl.cfc ColdFusion Component

  • <cfcomponent
  • displayname="FullGirl"
  • output="false"
  • hint="Handles all the girl functionality.">
  •  
  •  
  • <!--- Set unique ID for this instance. --->
  • <cfset VARIABLES.InstanceID = CreateUUID() />
  •  
  • <!--- Set default properties. --->
  • <cfset VARIABLES.Instance = StructNew() />
  • <cfset VARIABLES.Instance.FirstName = "" />
  • <cfset VARIABLES.Instance.LastName = "" />
  • <cfset VARIABLES.Instance.Gender = "Female" />
  •  
  •  
  • <cffunction name="Init" access="public" returntype="FullGirl" output="false"
  • hint="Returns an initialized Person instance.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="FirstName" type="string" required="false" default="" />
  • <cfargument name="LastName" type="string" required="false" default="" />
  •  
  • <!--- Store arguments. --->
  • <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
  • <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction name="EqualTo" access="public" returntype="boolean" output="false"
  • hint="Determines if this ">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="Comparable" type="any" required="true" />
  •  
  • <!--- Try to compare this object instance to the passed in. --->
  • <cftry>
  •  
  • <!--- Compare this ID to that ID. --->
  • <cfreturn NOT Compare(
  • VARIABLES.InstanceID,
  • ARGUMENTS.Comparable.GetInstanceID()
  • ) />
  •  
  • <cfcatch>
  •  
  • <!--- An error occurred, return false. --->
  • <cfreturn false />
  •  
  • </cfcatch>
  • </cftry>
  • </cffunction>
  •  
  •  
  • <cffunction name="GetFirstName" access="public" returntype="string" output="false"
  • hint="Returns first name.">
  •  
  • <cfreturn VARIABLES.Instance.FirstName />
  • </cffunction>
  •  
  •  
  • <cffunction name="GetGender" access="public" returntype="string" output="false"
  • hint="Returns gender.">
  •  
  • <cfreturn VARIABLES.Instance.Gender />
  • </cffunction>
  •  
  •  
  • <cffunction name="GetInstanceID" access="public" returntype="string" output="false"
  • hint="Returns the instance's unique ID.">
  •  
  • <cfreturn VARIABLES.InstanceID />
  • </cffunction>
  •  
  •  
  • <cffunction name="GetLastName" access="public" returntype="string" output="false"
  • hint="Returns last name.">
  •  
  • <cfreturn VARIABLES.Instance.LastName />
  • </cffunction>
  •  
  •  
  • <cffunction name="SetFirstName" access="public" returntype="void" output="false"
  • hint="Sets first name.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="FirstName" type="string" required="true" />
  •  
  • <!--- Set value. --->
  • <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction name="SetGender" access="public" returntype="void" output="false"
  • hint="Sets gender.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="Gender" type="string" required="true" />
  •  
  • <!--- Set value. --->
  • <cfset VARIABLES.Instance.Gender = ARGUMENTS.Gender />
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction name="SetLastName" access="public" returntype="void" output="false"
  • hint="Sets last name.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="LastName" type="string" required="true" />
  •  
  • <!--- Set value. --->
  • <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

Looking For A New Job?

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

Reader Comments

I appreciate you showing this, I always had a nagging voice in my head wondering if my making well structured code was secretly crippling performance. No worries now!

Reply to this Comment

@Ben,

I can't stress this enough:

Iteration performance tests are worthless.

If you want a true test to see which is more efficient, then you MUST do a proper load test in a proper environment.

How the hell do you know that a some other process running in the background on your machine interfered with these results? You don't.

I'm sorry it sounds like I'm yelling at you, but I can't stand the fact that every single person in the CF community thinks that writing a loop constitutes a true test of performance. IT DOESN'T.

Reply to this Comment

Hi Tony,

I agree that load testing will pick up on things that loop testing won't (especially as it relates to multiple thread and garbage collection and the like).

I don't think it is fair to talk about a process causing the disparity, though. That's why Ben ran the test multiple times.

FWIW, would love to see a post some time on best practices on (affordable) load testing for CF. Any postings you'd recommend or would you get a chance to do a quick posting yourself some time? I'd certainly love to learn how to do performance testing properly!

Reply to this Comment

Tony,

No worries on the tone; I am a fan of knowledge and truth. I would love to know how to test things in a more useful manner... I mean, that's why I do this type of stuff in the first place.

It makes me wonder though, what do you suppose the best use of the ColdFusion CFTimer tag would be? I pretty much only use it for testing in this fashion, but if this is not useful, I don't really see any use for the CFTimer tag. Any suggestions?

Reply to this Comment

Patrick,

But even regardless of the CFTimer tag and it's times... as Tony says, this type of testing isn't useful (CFTimer or end-start).

So my question is, if only true load testing is useful for performance testing, then what did the ColdFusion team have in mind when it created the CFTimer tag?

Reply to this Comment

@David,

No updates recently. Sadly, over two years later and I'm still not making full use of ColdFusion components. I need to up this effort!

Reply to this Comment

@Ben,

It's absolutely true that you cannot get accurate benchmarks simply by running stuff in a loop like this. However, you can certainly get good comparative performance numbers. You can get a good sense of whether one technique is generally faster than another technique, at least for the particular workload under test.

In other words, you cannot use this to determine in advance how many servers you will need. But you can use this to determine which of two techniques will be significantly faster than the other.

An easy example: ColdFusion arrays are actually Java objects under the hood. They have Java methods. To find out the size of a ColdFusion array, you can use either <code>ArrayLen(array)</code> or <code>array.size()</code> and you can use either <code>array[i]</code> or <code>array.get(i - 1)</code> to get the element of the array at the given index (using the 1-based indices familiar to ColdFusion). But the version using <code>ArrayLen</code> and <code>[i]</code> will turn out to be much faster. And the simplest way to find this out is to use a loop to get comparative performance numbers.

By the way, I like to use <code>t = GetTickCount(); /* do something many times in a long loop...; */ t = GetTickCount() - t;</code> to get performance numbers rather than using <code>cftimer</code>, because I can store the numbers in one file (Application.cfc) and display them later in another file (admin/timers.cfm) or log them or whatever.

Cheers,
Justice

Reply to this Comment

@Justice,

I am surprised that using the ColdFusion methods is faster than using the direct Java methods. I would think you'd be side-stepping some additional method calls if you go directly to the Java layer.

Reply to this Comment

@Ben,

Surprise! :P It actually makes a lot of sense when you see what's going on under the hood.

When you use Java methods in ColdFusion, ColdFusion has to go and do a bunch of reflection to find the right method, convert the parameters to the native Java types, call the method, etc. Reflection is slow.

But when you use <code>ArrayLen</code>, ColdFusion can "turn on" some optimizations. It will check that the object is of the right type and then it calls the <code>size</code> method directly, internally within the <code>ArrayLen</code> function. There is no reflection involved, so that makes <code>ArrayLen</code> significantly faster.

Because ColdFusion is a dynamic language (dynamic in the sense that functions are looked up by name-and-signature resolution/reflection, rather than by a single-instruction function pointer dereference), method invocation is slow. Not terribly slow, because ColdFusion, the Java libraries, and the Java runtime all try to cache some things to speed up reflection, but noticeably slow.

Calls to the built-in ColdFusion functions, however, are not dynamically bound. They are statically bound. In other words, it's the same as a single-instruction function pointer lookup from statically-typed languages. That makes the built-in ColdFusion functions much faster, because invoking them does not rely on reflection.

Cheers,
Justice

Reply to this Comment

@Justice, @David,

Right?!?! That is very fascinating. I really appreciate you clearing that up - I definitely thought just the opposite (not based on any evidence, just theory).

Reply to this Comment

Sidenote: Ben, just noticed your main page is /index.cfm and all of your entries and the member profiles rock a .htm extension.

What the what? Is that some kind of trick-people-and-computers-into-thinking-it's-1990-again caching or pyramid scheme technique you're using? What's going on? I'm guessing the htm files don't actually exist and that you're using url simulation like the majority of other sites in the world, but if so, why the .htm suffix? Very curious.

Reply to this Comment

@David,

The HTM files don't actually exist. I used to use 404-handling; now, I use URL rewriting with IIS-MOD-Rewrite. I chose the HTM file extension years ago before I had url rewriting skills and before ColdFusion has onMissingTemplate(). It was the only way I could "Catch" a missing page without ColdFusion invoking something internal.

Also, I was told a long time ago that htm files appear more "static" to Search Engines; but, that could just be old-school thinking.

Reply to this Comment

I find getTickCount() doesn't have enough granularity - it only deals with milliseconds.
I prefer to work with nanoseconds (1ms = 1,000,000ns)

Rather than executing something in a loop 1000x times with cftimer, I use the system class timer. I'm sure there are java overheads that get in the way, but gives a pretty good estimate. Average a few separate page executions for additional accuracy.

// a reference to the system class
sys = createObject('java','java.lang.System');

// get a 'tick count' in nanoseconds
ns = sys.nanoTime();

// do something eg.
sleep(1000);

// number of nanoseconds is the new 'tick' minus the old
ns = sys.nanoTime() - ns;

Reply to this Comment

@Mike,

Oh cool - I didn't know about that. I was not aware that we could get any values smaller than milliseconds.

Reply to this Comment

Hi Ben -- I just started getting into CFCs (yeah, I know, I'm about 8 years late to the party) and am really liking them, and would also like to take time here to offer a hearty thank-you to you for all the great advice and work you've done on your site.

My question -- in your example CFCs, I see you have the following:

<cfset VARIABLES.InstanceID = CreateUUID() />

In all the examples I've seen so far in the WACK book, it looks like we're supposed to use the var scope, i.e.:

<cfset var InstanceID = CreateUUID() />

Did you not care about this because this was only a test CFC? For best practice, unless you wanted to share the InstanceID variable to all methods in the object, you shouldn't do this, right? Of course, in your case here, I don't think this mattered at all.

- Sung

Reply to this Comment

@Sung,

Good question - the "var" keyword is only used within the confines of a CFFunction tag (it has not meaning elsewhere... with one exception). It's use is meant to define a variable in the "local" scope of a function. See, if you don't use it, and you set a variable inside of a function, the variable "leaks" into the variables scope of the component. So, if you had something like this:

<cffunction>
<cfset message = "hello" />
</cffunction>

... the variable "message" is actually getting stored into the variables scope of the component. This is essentially the same as this:

<cffunction>
<cfset variables.message = "hello" />
</cffunction>

In order to NOT alter the variables scope of the component, you use the VAR keyword to ensure that the variable is defined in the context of the CFFunction tag and NOT the greater CFComponent variables scope:

<cffunction>
<cfset var message = "hello" />
</cffunction>

Typically, variables that you define within a CFFunction are only meant to be used for the duration of the actual function. As such, you should use the "var" keyword to make sure you don't accidentally alter the component long-term. That is not to say that you always need to do that. If you want to alter the variables scope, you certainly can. The trick is just to know the difference.

I hope that helps a bit??

Reply to this Comment

Dear Ben,

Thanks, man -- yeah, it helps a lot! So have you still not gotten into using the "extends" property? I don't really get the attraction for it. I mean you can just CreateObject() whatever components you want inside a component, so what does the extends property really get you?

- Sung

Reply to this Comment

@Sung,

I use the Extends property when I need to create base functionality. My domain models haven't gotten too complex yet (still not whole-hog into OOP just yet). So for me, base functionality tends to revolve around implicit setter/getter methods via onMissingMethod() functionality. Other than that, I don't use it all that much.

Reply to this Comment

@Sung, (and maybe @Ben)

My co-worker and I have had troubles overwriting each other's work. We've got CFCs like "Functions.cfc" If I update it, I might overwrite some of his work, and vice versa.

Anyway, I'm hoping this could be a way we could break out our functions into "func1.cfc" and "Func2.cfc," "add_two_plus_two.cfc" etc... That way, when 2+2 suddenly equals five, I can update "add_two_plus_two.cfc" to reflect the new laws of physics.

Before I do this, though, I was hoping Sung might be able to confirm this is workable.

Reply to this Comment

@Randall

Use a version control system such as Subversion, Git, Mercurial, or Darcs (or others). That is the solution to your problem. Your suggestion will simply make your existing problem even worse.

Cheers,
Jay

Reply to this Comment

@Justice, I agree, but there's some hurdles to that -- money and red tape. Even if we did have CVS, the one I investigated was command line (the free version anyway). That's a hassle. I want to hit the save button and pop up a window, mostly pre-filled. Not only that, if he overwrites my work, we'd still have to figure out what was / wasn't changed. So I think I'd still want to do this even if we had a VCS like CVS, etc. It'd simplify tracking down the functions that don't work and replacing that one function (CFC file) with an older/stable version.

Reply to this Comment

@Randall,

Hmm, I am tending to agree with @Justice here. Is there any way that you can possibly just not work on the same components at the same time? Are there certain functions that one of you always works on?

Reply to this Comment

@Ben,

Absolutely, but that doesn't solve the problem. I'll work on CFC1-Funct1 and he'll work on CFC1-Funct3 at different times. If we have multiple release versions open -- say, "v3.3" and "v3.4" with production being v3.2, we have to make changes to 3.3 and replicate them to 3.4. But, if he changed v3.4-CFC1-Funct3 and I changed v3.3-CFC1-Funct1 and overwrite v3.4-CFC1.cfc, then his changes may get overwritten.

Now, I'm sure you're asking, "Why do you have mult versions open?" Just.... don't ask; It's the management. ;-) Again, we know a VCS would be the solution and being more cautious, but if we get in a hurry -- or don't think anything else changed -- mistakes happen.

We'll give this a shot on a handful of functions and see what happens.

Reply to this Comment

@Randall,

I am mostly saying this as a *joke* (not as advice), but if you have certain functions that YOU always work on and certain functions that someone else always works on, then you could always split the CFC and have one extend the other:

ActualCFC.cfc (extends >>)
RandallCFC.cfc (extends >>)
OtherPersonCFC.cfc

... then each of you could safely work on a sub-set of CFC methods.

Again, I am not really serious about this suggestion... but it would work.

Reply to this Comment

@Ben & @Mike,

A couple of things to keep in mind with Java in general with regards to time:

1. Java's standard timers have different accuracies on different OSes. For example, on Windows, most of the timers are only accurate to 15-16ms. Linux is generally accurate to 1 ms. This means that test accuracy relying on clocks are most likely only going to be accurate within this same window.

2. Java's "nanotime" calculations are only useful relative to one another. Also, within that, you have to be careful because the nanotime returns a long which rolls over relatively often, so over a long test run, you need to do tests of the nanotime values being compared to ensure that a roll over didn't occur. If a roll over does occur, the subtraction method will throw your test results / timing way off.

I'm not 100% sure how heavily ColdFusion uses Java's timing, but those are a couple of gotchas that I have seen hurt Java programmers pretty regularly when trying to deal with tight timings.

Reply to this Comment

@Steven,

Good point with the timer accuracy. Othe people have also pointed out that there is only so much that you can actually glean from this type of performance testing (testing withOUT load). At least I'm not seeing any red flags with this approach, which has to be a good sign even without the accuracy.

Reply to this Comment

I just discovered that our system has a WHOLE LOT of

  • <cfobject type="component" name="something" component="comp">
  • <cfobject type="component" name="something" component="comp">
  • <cfobject type="component" name="something" component="comp">

In other words, the same thing is repeated seemingly unnecessarily.

Does this cause a performance hiccup? Extra memory usage?

Anyone?

Reply to this Comment

Yes, it runs each line like any other code so unless the underlying code checks for some state change it'll cost you the full server resources of running it x ( 3 ) times.

It looks like you had a super OCD programmer to be honest. Get him some pills or something hahahahah.

Reply to this Comment

Slightly new topic - I have three CFCs:

C extends B
B extends A
A is the head honcho.

Can A reference functions in B & C if they're private?

I did some tests yesterday and I think the answer is *NO*, but had trouble finding the answer on the series of tubes, and wondered if I was doing something incorrectly.

To me, it seems like a component (A) with extension functions (B.cfc & C.cfc) should be able to use the functions inside B & C since they're children.

Or should I think of this in reverse? Thus write my functions in C, then refer to the "super" functions of B and A?

Reply to this Comment

Sadly, no.

Inheritance is one way, downstream.

C inherits all of B and A's functions, B all of A's, but A was basically a sperm donor, dropping his genes and disappearing. A can never use any of the functions from any of its children or grandchildren via inheritance.

Any common functionality that you want to use across all 3 needs to be moved further upstream to A.

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.