Just to set a baseline, what Henry was asking is whether there are performance considerations when contrasting these two approaches to composing a component:
<!--- MethodsInline.cfc --->
<cfcomponent>
<cffunction name="publicMethod" access="public" returntype="struct" hint="This is my public method">
<cfreturn arguments>
</cffunction>
<cffunction name="privateMethod" access="private" returntype="struct" hint="This is my private method">
<cfreturn arguments>
</cffunction>
<cffunction name="packageMethod" access="package" returntype="struct" hint="This is my package method">
<cfreturn arguments>
</cffunction>
<cffunction name="remoteMethod" access="remote" returntype="string" hint="This is my remote method">
<cfargument name="who" type="string" required="true">
<cfreturn "hello, #arguments.who#">
</cffunction>
</cfcomponent>
<!--- MethodsViaInclude.cfc --->
<cfcomponent>
<cfinclude template="./methodsViaInclude.cfm">
<cffunction name="inlineRemoteMethod" access="remote" returntype="string" hint="This is my remote method">
<cfargument name="who" type="string" required="true">
<cfreturn "hello, #arguments.who#">
</cffunction>
</cfcomponent>
<!--- methodsViaInclude.cfm --->
<cffunction name="publicMethod" access="public" returntype="struct" hint="This is my public method">
<cfreturn arguments>
</cffunction>
<cffunction name="privateMethod" access="private" returntype="struct" hint="This is my private method">
<cfreturn arguments>
</cffunction>
<cffunction name="packageMethod" access="package" returntype="struct" hint="This is my package method">
<cfreturn arguments>
</cffunction>
<cffunction name="includedRemoteMethod" access="remote" returntype="string" hint="This is my remote method">
<cfargument name="who" type="string" required="true">
<cfreturn "hello, #arguments.who#">
</cffunction>
The first file listing has the methods inline; the second two file listing compose a CFC via shell CFC and then including a file with the actual methods.
The first thing to consider is the answer to the actual question: performance.
Well one thing that warrants mention here is that ColdFusion didn't actually execute the CFM files, all that happens to them is that they are compiled, and then the resultant class files are loaded and executed. The compile process happens one when the CFML file is first hit, and then each time the file changes, or if for some reason the compiled class is removed from memory, and if the class file had not been compiled to disk. By default, the compiled classes are saved to [ColdFusion dir]\WEB-INF/cfclasses (on my machine that's C:\Apps\JRunservers\cf9.local\cfusion.ear\cfusion.war\WEB-INF\cfclasses). Given how this works, the composition of the CFML files do not contribute a huge amount to this sort of performance: a file will be read far far more often than it is changed (necessitating a recompile), and if your server is configured properly, most of your classes will stay in memory once they're needed anyhow.
For completeness, one should consider the compiled class files too, in case there's any massive overhead presented by one or the other approach. I'm not going to reproduce the decompiled code here (because it's not the clearest code in the world, and is not very helpful to look at), but it's mostly the same. As far as the files themselves go, we end up with this:
cfMethodsInline2ecfc876167990$funcPACKAGEMETHOD.class
cfMethodsInline2ecfc876167990$funcPRIVATEMETHOD.class
cfMethodsInline2ecfc876167990$funcPUBLICMETHOD.class
cfMethodsInline2ecfc876167990$funcREMOTEMETHOD.class
cfMethodsInline2ecfc876167990.class
cfMethodsViaInclude2ecfc1581372422$funcINLINEREMOTEMETHOD.class
cfMethodsViaInclude2ecfc1581372422.class
cfmethodsViaInclude2ecfm1581372444$funcINCLUDEDREMOTEMETHOD.class
cfmethodsViaInclude2ecfm1581372444$funcPACKAGEMETHOD.class
cfmethodsViaInclude2ecfm1581372444$funcPRIVATEMETHOD.class
cfmethodsViaInclude2ecfm1581372444$funcPUBLICMETHOD.class
cfmethodsViaInclude2ecfm1581372444.class
cfremote2ecfm1868968566.class
Note that there's one file for each CFC, one file for each method, and an extra file for the included file. And also one for my calling code. So there's an extra file if one choses to include the methods. I would not consider this a meaningful overhead. Or any sort of overhead.
I didn't bother actually contriving some sort of performance test here, because I think it's clear there is not going to be any real difference. Trying to optimise this sort of performance falls under the realms of premature optimisation. Or: "simply not worth considering".
So performance isn't a consideration. What other considerations are there. Well: the two approaches are not actually the same, and the include-your-methods approach has significant draw backs.
Look at this code, and its output:
<cfscript>
objects.o1 = createObject("MethodsInline");
metadata.o1 = getMetadata(objects.o1);
objects.o2 = createObject("MethodsViaInclude");
metadata.o2 = getMetadata(objects.o2);
writeDump(var=metadata.o1, label="metadata.o1");
writeOutput("<br/><br/>");
writeDump(var=metadata.o2, label="metadata.o2");
</cfscript>
metadata.o1 - struct | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
EXTENDS |
| |||||||||||||||||||||||||||||||
FULLNAME | blog.includedMethods.MethodsInline | |||||||||||||||||||||||||||||||
FUNCTIONS |
| |||||||||||||||||||||||||||||||
NAME | blog.includedMethods.MethodsInline | |||||||||||||||||||||||||||||||
PATH | D:\websites\www.scribble.local\blog\includedMethods\MethodsInline.cfc | |||||||||||||||||||||||||||||||
TYPE | component |
metadata.o2 - struct | |||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
EXTENDS |
| ||||||||||||||||||||||||||||
FULLNAME | blog.includedMethods.MethodsViaInclude | ||||||||||||||||||||||||||||
FUNCTIONS |
| ||||||||||||||||||||||||||||
NAME | blog.includedMethods.MethodsViaInclude | ||||||||||||||||||||||||||||
PATH | D:\websites\www.scribble.local\blog\includedMethods\MethodsViaInclude.cfc | ||||||||||||||||||||||||||||
TYPE | component |
(I've collapsed a bit of the first dump to save space, but you get the idea)
The second object is missing the bulk of its metadata: none of the methods from the include are displayed.
If you're writing an API, this has a knock-on effect that the docs CF generates for you are also truncated (because they're based on the metadata):
The documentation for MethodsInline.cfc are complete:
blog.includedMethods.MethodsInline
Component MethodsInline
hierarchy: | WEB-INF.cftags.component blog.includedMethods.MethodsInline |
path: | D:\websites\www.scribble.local\blog\includedMethods\MethodsInline.cfc |
serializable: | Yes |
properties: | |
methods: | packageMethod, privateMethod*, publicMethod, remoteMethod |
packageMethod |
---|
package struct packageMethod ( ) This is my public method Output: |
privateMethod* |
private struct privateMethod ( ) This is my private method Output: |
publicMethod |
public struct publicMethod ( ) This is my public method Output: |
remoteMethod |
remote struct remoteMethod ( ) This is my public method Output: |
The documentation for MethodsViaInclude.cfc are sadly lacking:
blog.includedMethods.MethodsViaInclude
Component MethodsViaInclude
hierarchy: | WEB-INF.cftags.component blog.includedMethods.MethodsViaInclude |
path: | D:\websites\www.scribble.local\blog\includedMethods\MethodsViaInclude.cfc |
serializable: | Yes |
properties: | |
methods: | inlineRemoteMethod |
inlineRemoteMethod |
---|
remote string inlineRemoteMethod ( required string who ) This is my remote method Output: Parameters: who: string, required, who |
This is still a bit "cosmetic-y". Here's an actual function problem.
Consider this code, making a call to the CFC's remote methods:
<cfscript>
webservices.oMethodsInlineWs = createObject("webservice", "http://www.scribble.local/blog/includedMethods/MethodsInline.cfc?wsdl");
results.fromInlineWebService = webservices.oMethodsInlineWs.remoteMethod(who="Tim");
writeDump(var=results.fromInlineWebService, label="results.fromInlineWebService");
webservices.oMethodsViaIncludeWs = createObject("webservice", "http://www.scribble.local/blog/includedMethods/MethodsViaInclude.cfc?wsdl");
try {
results.fromIncludeWebService = webservices.oMethodsViaIncludeWs.includedRemoteMethod(who="Graeme");
writeDump(var=results.fromIncludeWebService, label="results.fromIncludeWebService");
} catch (any e){
results.fromIncludeWebService = "#e.message# #e.detail#";
}
results.fromIncludeWebServiceInlineMethod = webservices.oMethodsViaIncludeWs.inlineRemoteMethod(who="Bill");
writeDump(results);
</cfscript>
You can probably guess from my usage of try/catch in that where I'm going with this. Here's the output:
struct | |
---|---|
FROMINCLUDEWEBSERVICE | Web service operation includedRemoteMethod with parameters {who={{who, Graeme}}} cannot be found. |
FROMINCLUDEWEBSERVICEINLINEMETHOD | hello, Bill |
FROMINLINEWEBSERVICE | hello, Tim |
CF doesn't expose the "remote" method in methodsViaInclude.cfm as remote. This can also be borne out by looking at the WSDL, which only shows the inline remote method (I'll spare you the output of the WSDL!).
I also tried this test with the private method, but that worked fine. That's something. I did not test with package methods, but I would guess they'd be fine too, as I suspect it's the way that CF looks for web services that is at fault here, rather than the access levels intrinsically.
Another consideration is that the include approach kinda breaks CFC inheritance a bit. Look at this example:
<!--- Parent.cfc --->
<cfcomponent>
<cffunction name="someMethod">
</cffunction>
</cfcomponent>
<!--- Child.cfc --->
<cfcomponent extends="Parent">
<cfinclude template="childMethods.cfm">
</cfcomponent>
<!--- childMethods.cfm --->
<cffunction name="someMethod">
</cffunction>
<!--- testOverride.cfm --->
<cfset o = createObject("Child")>
<cfdump var="#o#">
Running testOverride.cfm yields this compile error:
The following information is meant for the website developer for debugging purposes. | ||||||
Error Occurred While Processing Request | ||||||
|
This is because of the way CF compiles different file types: CFM files don't have the notion of object orientation, so inheritance and method overriding is irrelevant, so when including a CFM file, it breaks.
Another reason still is in CF Builder, the "Outline" view doesn't list all the methods in the CFC if all the methods are included. Well: it does list all the methods in the CFC, but that's not all the methods that comprise the component though.
All in all... I reckon it's a shoddy practice, and causes problems.
--
Adam