I have a “best-practices” question in regards to the correct way to instance CFCs that all need to talk to each other in a given project.
Let’s say for example you have a web application that has a bunch of different modules in it:
Each of these modules is nicely organized so that the functions that pertain to each module are contained within separate CFC files:
Each CFC contains functions appropriate for that particular module. For example, the Users.cfc contains functions pertaining to logging users on/off, updating account info etc… Sometimes a CFC might need to reference a function in another CFC, for example, if the store (Store.cfc) needs to get information from a customer (Users.cfc). However, I'm not sure of the correct way to accomplish this. There are a couple ways that I've been playing with to allow my CFC's to reference each other:
Method 1: Within a CFC, instance the other CFC’s that you’re going to need:
<!--- Store.cfc --->
<cfcomponent>
<!--- instance all the CFC’s we will need here --->
<cfset usersCFC = CreateObject("component","users") />
<cfset filesCFC = CreateObject("component","files") />
<cffunction name="storeAction">
<cfset var customerInfo = usersCFC.getUser(1) />
This approach seems to work most of the time unless some of the instanced CFC’s also instance the CFC’s that instance them. For example: If Users.cfc instances Files.cfc and Files.cfc also instances Users.cfc. I’ve run into problems with occasional dreaded NULL NULL errors with this probably because of some type of infinite recursion issue.
Method 2: Instance any needed CFCs inside a CFC’s function scope (this seems to prevent the recursion issues):
<!--- Store.cfc --->
<cfcomponent>
<cffunction name="storeAction">
<!--- create a struct to keep all this function’s variables --->
<cfset var local = structNew() />
<!--- instance all the CFC’s we will need here --->
<cfset local.usersCFC = CreateObject("component","users") />
<cfset local.filesCFC = CreateObject("component","files") />
<cfset var customerInfo = local.usersCFC.getUser(1) />
My concern with this approach is that it may not be as efficient in terms of memory and processing efficiency because you wind up instancing the same CFC’s multiple times for each function that needs it. However it does solve the problem from method 1 of infinite recursion by isolating the CFCs to their respective function scopes.
One thing I thought of based on things I've seen online and articles on object oriented programming is to take advantage of a “Base.cfc” which uses the “extends” property of the cfcompontent tag to instance all of the CFC's in the application. However, I've never tested this type of setup before and I'm not sure if this is the ideal way to allow all my CFCs to talk to each other especially since I believe using extends overwrites functions if any of them share a common function name (e.g. "init()").
<!--- Base.cfc --->
<cfcomponent extends="calendar store blog users files">
What is the correct "best-practices" method for solving this type of problem?
If each of your CFC instances are intended to be singletons (i.e. you only need one instance of it in your application), then you definitely want to looking into Dependancy Injection. There are three main Dependancy Injection frameworks for CF; ColdSpring, WireBox and DI/1.
I'd suggest you look at DI/1 or WireBox as ColdSpring hasn't been updated for a while.
The wiki page for DI/1 is here: https://github.com/framework-one/di1/wiki/Getting-Started-with-Inject-One
Wirebox wiki page is here: http://wiki.coldbox.org/wiki/WireBox.cfm
Essentially what these frameworks do is to create (instantiate) your CFCs (beans) and then handles the dependancies they have on each other. So when you need to get your instantiated CFC it's already wired up and ready to go.
Dependancy Injection is also sometimes called IoC (inversion of control) and is a common design pattern used in many languages.
Hope that helps and good luck!