So I could have sworn I had blogged on this before, but I couldn't find it in my archives, so here goes. (And if I do start repeating myself, let me know folks!)
Is there a reason to favor cfinvoke over cfobject? Does cfinvoke clean up after itself faster?
Technically you are comparing apples and oranges. The cfobject tag (and createObject function) are meant to create an instance of a CFC. The cfinvoke tag runs a method on a CFC. You can use createObject to both create a CFC and run a method:
<cfset foo = createObject("component", "president").init(42)>
But the same could be done with cfinvoke.
<cfinvoke component="president" method="init" id="42" returnVariable="foo">
I think you will find it comes down to a style issue.
Archived Comments
The CFOBJECT and createObject tag may be locked down for security reasons, where your only option may be CFINVOKE for components.
Another way to put it is when you use cfinvoke on a component directly, without creating an object out of it first, you are keeping it stateless, meaning that any of your initialization code and local code has to be ran by all of the methods. If you create an object out of it first, you can run an init method while creating it and get all the local variables (local to the object) created and ready before you call any of your methods. Also, remember that you can call cfinvoke on an object that you have already created. then it just becomes a difference of cfset myresult = myobject.mymethod(myarguments) to cfinvoke component="#myobject#" returnvariable="myresult" method="mymethod"
cfargument name="myargumentname" value="myargument" /
/cfinvoke
sorry if that makes it hard to read, i left out the greater than less than signs so it wouldnt get stripped.
Well said, Ryan.
In addition, by instantiating an object and then calling the objects methods it creates less load on server for garbage collection. If a site that has a lot of traffic instantiates an object in the application scope (only do this for objects that do not need to maintain instance "state" data).. its a lot less work for the server than constantly invoking arguments over and over again.
My $.02
The tip of reusing a CFC instance is not something I covered in this entry since I didn't think it was on topic. Do you think an entry on it (even though it's simple) would be a worthwhile blog entry?
I might as well mention; I recently changed BlogCFC so it could work on a host that does not allow createObject(). I replaced the createObjects with cfinvoke, using the init() approach... and bob's your uncle.
If you're writing your CF for portability (on to cheap hosting solutions for example) then this is a reason to use cfinvoke only.
Perhaps there should be a createComponent() function. So it can be differentiated from the java stuff in the Sandbox.
Ray,
I think that would help clear the water a bit more and give some of us a refresh.
@Ryan, I'm not sure I follow your stateless example in regards to initilization code and local code having to be run if you use cfinvoke.
I think I'm confused on the "instance state"
Lets say for example that you have a method in your cfc that takes in the datasource name you are working with and stores it locally. You would want to put this in an init method. If you do this, and you create an object out of the component, you can access that local variable (local to the object itself) in all of your other methods, keeping you from having to pass in the datasource name to each of your methods.
When you set variables local to an object and create an object out of it, it is said to have state. When you just call a component directly through cfinvoke, without first creating an object out of it, it is said to be stateless, meaning it has no "instance" variables.
Generally, stateless is fine for "services", or components that do not need to "know" anything, they just take arguments and do something with them. But if you want your component to know something, that is, you tell it once and it remembers it, then you are going to have to create an object out of it.
For example, to make my code portable between our development and production environments, I always pass in the datasource that an object should use when I create it, and then for the rest of my app where I am using that object, I dont have to pass in the datasource any more.
Does that make more sense or is it still clear as mud?
I also want to respond to Christopher Wigginton's comment that cfobject and createObject may be locked down. Generally this is done more so to keep you from being able to create java and com objects. But there is more than just a syntatical difference between createObject and cfInvoke, while related they do two completely different things. If you are going to get into any Object Oriented programming at all, you are going to need object support. I definately suggest getting a new host if that is the case.
Ray, I think one of the key problems relative newbies to CFMX have is the ins and outs of Components. What are some of the ways that experienced programmers have found works best (three components for each like you did here, etc.), what are the guidelines (how long should my cfc be before I think about breaking it up, how should I use the Psuedo-constructor and "Init" method, etc.). Now that "Experienced" CFMX programmers have been using components for a couple of years it may be time for a book, or a paper or an article or a blog entry. I have been working on cfmx for about a year now and I'm still using the This scope in my cfc because I didn't know any better and i'm too far into the three projects I'm working on... Don't let others suffer the same fate as me!!!
Yes, I remember talking about this before, but my legs are wobbly when I talk about this subject.
Here's what I do:
<cfset xxxObj = CreateObject("Component","Components.xxx").Init("myDataSource")>
<cfset xxxQry = xxxObj.View1()>
<cfdump var="#xxxQry#">
xxx.cfc contains:
<cfcomponent displayname="xxx" output="False">
<cffunction name="Init" output="true" returntype="components.xxx">
<cfargument name="DSN" required="yes">
<cfset Variables.DSN = arguments.DSN>
<cfreturn this>
</cffunction>
<cffunction name="View1" output="False" returntype="query" hint="I return everything from xxxView">
<cfargument name="xxxID" type="numeric" required="no">
<cfset var rst = "">
<cfquery name="rst" datasource="#Variables.DSN#">
SELECT * FROM xxx
<cfif isDefined("arguments.xxxID") and isValid("integer",arguments.xxxID)>
WHERE xxxID = #arguments.xxxID#
</cfif>
</cfquery>
<cfreturn rst>
</cffunction>
</cfcomponent>
The thinking is:
ANYTHING THAT HAS TO DO WITH TABLE xxx GOES THROUGH THIS CFC.
So my question is:
How would you rewrite line 2:
<cfset xxxQry = xxxObj.View1()>
using cfinvoke?
Phillip, the only way to do that would be to rewrite your view1 method to take an argument of dsn. Then you could do
<cfinvoke component="components.xxx" method="View1" returnvariable="xxxQry">
<cfinvokeargument name="dsn" value="myDatasource" />
</cfinvoke>
But this illustrates the point that if you have many methods in your component that all need the same dsn, why not create and object out of it while passing in the dsn, and then use it throughout your component.
While both methods may be <em>functionally the same</em> they are quite different.
Right.
For the sake of simplicity, I left out the following functions in xxx.cfc:
<cffunction name="View2" output="False" returntype="query" hint="I return a list starting from xxxID">
</cffunction>
<cffunction name="NextRecord" returntype="numeric" output="false" hint="I return the next xxxID">
</cffunction>
<cffunction name="PrevRecord" output="False" returntype="numeric" hint="I return the Prev xxxID">
</cffunction>
The idea is that you don't have any cfquery commands in your programs. If you need something from table xxx, you have to create a function and either return a query or return a primary key ID.
Ryan,
You mentioned earlier on that I can call cfinvoke on an object that's already in existance. This would mean that I can now pass named values to functionS rather than relying on position as I normally would?
Could that lend itself to the thought that because the arguments are named the function call would be quicker, as well as more flexible?
First, let me say that cfinvoke is not the only way you can passed named arguments to a function. If I have a function like so:
<cffunction name="myFunct">
<cfargument name="one" required="True" />
<cfargument name="two" required="true" />
</cffunction>
I can call that function like this:
<cfset myFunct(two=a,one=b) />
Which gives you named parameters. This way of doing things can also be used for providing values for non-required arguments.
Also, you can pass argument collections into functions, which is also passing them in named as well in a sense. For the function above I could call it like so:
<cfset myArgs = structNew() />
<cfset myArgs.two = a />
<cfset myArgs.one = b />
<cfset myFunct(argumentCollection=myArgs) />
Now, I much prefer the cfinvoke syntax because I believe it to be easier to read, even though it is a little more typing.
As far as speed, I doubt the difference in any of these methods really make much difference. Named or not, the functions would probably run about the same. To me, no matter which one ran faster though, the readability that named parameters gives you is worth it.
But try all the different methods and time them to see what you get, if you are really worried about performance. But to me, ease of maintenance is my first priority.
Hum.. excuse me for adding comment to this 3-yrs-old blog entry, but...
Doesn't CFINVOKE create an instance of the component before invoking the method anyway? I believe it does, and therefore I do not think CFINVOKE has any performance advantage over CreateObject()
e.g.
<!-- x.cfc -->
<cfcomponent>
<cfscript>
variables.x = 123; // sudo-contructor init
function getX(){
return variables.x;
}
</cfscript>
</cfcomponent>
<!-- getX.cfm -->
<cfset x = createObject("component","x").getX()
<!--- should be the same as --->
<cfinvoke component="x" method="getX" returnvariable="x">
Notice that if cfinvoke is really just invoking the method getX, it should not have been able to get x, since x belongs to the Variables scope (a scope that's created once the component has been instantiated).
CFINVOKE _can_ create a new instance. It does not _HAVE_ to.
<cfinvoke component="#foo#" ...>
will work with a CFC already created.
Oops.. I should specify that.. in the case of invoking a 'static' method from a 'static' class.
Although there's no 'static' class in CF, but like when you have a helper method that get called throughout many objects, but you don't want to use <cfinclude> to inject that method. Then, in that case, use of createObject() then invoke should be same as cfinvoke that method directly.
Well, if I get you right, I'd still create the CFC once. CFC creation, while much improved in CF8, can still be slow. Make it once in onApplicationStart and use the instance from then on.
What happens if you run the init method of a CFC more than once? Will it keep creating new objects in memory?
What if you have two applications that want to be able to use the same CFC, can one application create the object, and the other application use the object?
"What happens if you run the init method of a CFC more than once? Will it keep creating new objects in memory?"
Yes. If thats what you want.
"What if you have two applications that want to be able to use the same CFC, can one application create the object, and the other application use the object?"
Yes - but you need to modify how you do things. The best way to handle this is with ColdSpring: http://www.coldspringframew...
It can manage creating singletons for you.
Hi. I just have read the post and the comments. these are the great. but I am just trying to evaluate at a problem while changing the [CreateObject]s into the [cfinvoke]s.
Pretty obvious that I could change all the stuff where I found the createObject as follows
<cfset xxx = CreateObject("component","cfc.xxx").init("requiredparam") />
simply to the cfinvoke with the method attribute and cfinvokearguments within it.
But what if I have to change the following statement to the cfinvoke with no calling method. what would you recommend to do..
<cfset obj_xxx = CreateObject("component","cfc.xxx") />
If you noticed the above statement initiating the component but is not calling the init method (which actually does have one required parameter as below)
<cfcomponent displayname="xxx" output="false">
<cffunction name="init" access="public" returntype="xxx" output="false">
<cfargument name="required_param" type="cfc.xxx2" required="true" />
<!--- other work --->
<cfreturn this />
</cffunction>
</cfcomponent>
Above context works great but with CreateObject as it just returns the instance of the component and I can use it later on the page.
I am just not able to figure it out how can I use <cfinvoke> to get instance of the component as I don't have any parameter of type "xxx2" to send in to the init method.
Hope I tried good to reproduce the problem.
Regards
Khurram
In your case you are out of luck. Since the init() function requires an arg you don't want to send, you can't use it. If you want, you can always make a new method, perhaps even called "new", and in that let it simply return this.
Thanks Raymond,
I actually don't require to use the init method at all. It was a case what I felt being trouble and the I just managed to prepare a required parameter to send in. But right after that I thought of making the new method just as you told above. I did it as below..
<cffunction name="doNothingButReturnThis" access="public" returntype="xxx" output="false">
<cfreturn this />
</cffunction>
So would it be perfectly equal to the CreateObject (without init method) using cfinvoke with this above method
Was that a question or a statement? Either way - yes. :)
This was a question afcourse. Just forgot to put the Question Mark perhaps :D
And I was just not sure what I did is would be the same as createObject in the listed scenario. So I had to ask this question to be sure.
So thanks alot. now after your "Yes" I am sure that I can have it like that :)
Have a question on the performance.
Read an article the other day on how objects get instantiated in ColdFusion, and it was said that each method maps to an object when actually complied.
That said will it be safe to assume that Using cfinvoke to access a method on a CFC would be faster than actually creating an instance then accessing the method.
Assuming the CFC is of a services type & doesn't need any properties
When you use cfinvoke to call a method on a CFC and use the name of the CFC, the CFC is instantiated anyway.