Ronan asked the following question:
I´ve read some messages talking about resources used by Coldfusion (and the Java) to instantiate objects (CreateObject function or cfinvoke tag).Is it possible to an application becomes slow because the obsessive use of the OOP or many objects instantiated per template?
While I wouldn't say OOP is the cause, I can say I've seen excessive use of CreateObject slow a site down. I almost always recommend that you createObject once, typically in the Application.cfc onApplicationStart method. If you are using CFMX instead of CFMX7, simply use the "old style" of checking for an application flag. For an example of this, check out BlogCFC's Application.cfm file. I create a few objects, some of which are quite big. (The core blog.cfc file is way too big.) By ensuring I only create these files once, I can dramatically improve my page to page performance.
As a general FYI, there are a number of steps you can do to track down slow parts of your application. First - turn on the "Log Slow Pages" option in the admin and try to figure out what pages are the culprits. Make use of the cftimer tag to isolate what parts of your files are slowest. Check your SQL queries to ensure your database queries aren't performing poorly.
Basically, you need to get down and dirty and dig around all your files. I've said in the past it is always a good idea to have a very strong understanding of what your application does in every request. This will be a big help as well.
Archived Comments
Hi Ray,
Just to follow up, it is important to distinguish between singletons and business objects.
You are probably going to have a bunch of services, DAO's and maybe gateways for your application. They are like object specific function libraries which you can just load and forget in application.cfc.
You will also have stateful business objects (perhaps a user or a list of articles) which have different scopes. A user or shopping cart may be session scoped whereas display information (perhaps a list of articles or products) will often be request scoped.
It is possible that if you create an array of objects for all of your display lists, you may have performance problems. If you don't need the encapsulation of methods provided by getters and setters, just use query recordsets instead. If you DO need the encapsulation, consider creating a single business object, loading it with your query and providing iterator functions. Certainly instantiating one (or a handful) of request scoped objects is unlikely to be a performance problem.
Also, on the whole it is better to write for maintainability (assuming the applicaiton will be around for a while) and to refactor for performance IF you run into problems. Otherwise you're in danger of premature optimization!
Best Wishes,
Peter
All good points. You definitely want to watch what your cache. I was definitely referring to Application scope objects, as Session based objects could get insane with robots. Michael Dinowitz has some good resources for dealing with robots and sessions.
To specifically answer the original question, yes. ColdFusion will slow to a crawl due to "obsessive" use of OOP practices. Additionally, many objects instantiated per template is frequently a major cause of application slowdown. ColdFusion's OOP-like implementation of CFCs are poor substitutes for the real deal. Native OO languages (like Java) are designed to handle lots of objects flying around in the code. ColdFusion is not.
Before jumping on the OO bandwagon in CF, you need to have a good reason. You may lose much of the RAD and simplicity while introducing a whole new world of problems.
NAT
If done right, CFCs can offer a incredible performance boost to an application. I'm going to start blogging about the performance gains we are experiencing since we converted BannerBoxes over to CFCs. Check it out.
Nat, while I think you are right, I don't think it is the "obsessive" use of OOP, but just the improper use. Anything can be done improperly. However, there is probably a bigger chance of it happening with CFers going to OOP if they are new to the concepts.
Ray, thank you for you answer.
What do you suggest for...
I used to create many classes for the most used tables (clients, products, categories, cities and so on).
Usually, each class has two methods: Load() and List(), although some tables have a few more widely used methods.
Since I started working this way I´ve noted improvement in the development productivity, once it provides a cleaner and centralized code.
OK, let´s see an example:
When a user register a product, the template instantiate some classes (around 10) used to lists the combobox options.
The same situation happen on the product searchs.
What do you suggest for keep the code maintenability and have good performance of the website?
Ronan, I may not be getting you. It sounds like you are using CFCs already, and have already noticed a productivity improvement. Are you asking if you should be caching those classes you create? Well, if Load() loads instance data into the CFC, then you can't, since you need them to be unique. If Load() returns a struct, then you could, since the CFC would be more a service. It sounds like you may be mixing a "Instance" CFC (Load) and a "Service" CFC (List) together. Maybe consider breaking them up so that your CFC which does List ops, and other NON instance ops, into a fooService.cfc.
I break up my CFCs into Beans, DAOs, and Gateways. The Bean holds one record. The DAO does CRUD, and the Gateways to list() + other misc functions. If you download some of my projects you will see this in use. (But not in BlogCFC.)
Ray,
> It sounds like you are using CFCs already, and have already noticed a productivity improvement.
Yes.
> Are you asking if you should be caching those classes you create?
Perhaps. I´m asking for the ways to keep the application fast without loosing the power reusability of the OOP.
I know it isn´t a simple question... :-)
cache the classes could be one way, as you said, but it wont works in all situation.
OK, now answering your questions:
Load() loads the instance data into the CFC properties and List() returns a query with the options.
<cfcomponent hint="Class Products">
<cfscript>
THIS.id;
THIS.name;
THIS.group;
THIS.category;
</cfscript>
<cffunction name="Load">
<cfargument name="id" type="numeric" required="yes" />
<cfset var q_GetData = "" />
<cfif IsDefined("arguments.cod") and IsNumeric("arguments.id")>
<cfquery name="q_GetData" datasource="#dsn#">
SELECT *
FROM products
WHERE id = <cfqueryparam value="arguments.id" cfsqltype="cf_sql_integer" />
</cfquery>
<cfscript>
THIS.id = q_GetData.id;
THIS.name = q_GetData.name;
THIS.group = q_GetData.group;
THIS.category = q_GetData.category;
</cfscript>
</cfif>
</cffunction>
<cffunction name="Load">
<cfargument name="id" type="numeric" required="yes" />
<cfset var q_Options = "" />
<cfquery name="q_GetData" datasource="#dsn#">
SELECT id, name, group, category
FROM products
</cfquery>
<cfreturn q_Options />
</cffunction>
</cfcomponent>
Having classes like these, I use to instantiate it in the templates, for example, if I have a form that asks for cliente, product and city:
<cfcomponent hint="Order form">
<cffunction name="OrderController" returnvariable="struct">
<cfset var stReturn = StructNew() />
<cfset var oClient = CreateObject("component", "classes.clients") />
<cfset var oProduct = CreateObject("component", "classes.products") />
<cfset var oCity = CreateObject("component", "classes.cities") />
<cfset stReturn.q_Clients = oClient.List() />
<cfset stReturn.q_Clients = oProduct.List() />
<cfset stReturn.q_Clients = oCity.List() />
<cfreturn stReturn />
</cffunction>
</cfcomponent>
If I need to use the client informations in the template:
<cfcomponent hint="View Client Details">
<cfset THIS.oClient = CreateObject("component", "classes.clients") />
<cfset THIS.oCategory = CreateObject("component", "classes.categories") />
<cffunction name="CallController">
<cfargument name="clientId" type="numeric" required="yes" />
<cfif IsNumeric(arguments.clientId)>
<cfset THIS.oClient.Load(clientId) />
<cfset THIS.oCategory.Load(THIS.oClient.category)
</cfif>
</cffunction>
</cfcomponent>
So in the template I can use:
------------------------
<cfset oController = CreateObject("component", "controllers.ViewClientDetails") />
<cfset oController.CallController(URL.clientId) />
Name: <cfoutput>#oController.oClient.name#</cfoutput>
Category: <cfoutput>#oController.oCategory.name#</cfoutput>
------------------------
In many cases I need to instantiate much more objects (around 10)
I know I could use a controller method containing just a query joining the table informations and returning it, but I would loose productvity, code clarity, maintenability and abstraction.
I also thought, as you said, to keep a big one component just for List methods in a persistent scope. I´m sure that it would be help in any way, but the problem I see is each time the code or applications grows or a table is added, It would need to be reloaded.
Anyway, I´ll download one of your projects to study it understand better your thoghts.
Thank´s in advance Ray,
Wow, big comment. I didn't mean one component for ALL list methods. I meant one for each content type.
So for example, imagine my site has Spaceships and Fruit. I normally have the 3 CFCs I mentioned above. So I'd have 6 CFCs total. If I wanted to get all the fruit, I'd use the FruitGateway.cfc.
Definitely take a look at a few of my projects, it will help. (Not that I'm the best.)