Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the correct way to allow different ColdFusion CFC's to instance each other?

Tags:

coldfusion

cfc

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:

  • Online Calendar
  • Online Store
  • Blog
  • File Manager (uploading/downloading/processing files)
  • User Accounts

Each of these modules is nicely organized so that the functions that pertain to each module are contained within separate CFC files:

  • Calendar.cfc
  • Store.cfc
  • Blog.cfc
  • Files.cfc
  • Users.cfc

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?

like image 766
Dave L Avatar asked Nov 07 '14 16:11

Dave L


2 Answers

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!

like image 86
John Whish Avatar answered Sep 20 '22 16:09

John Whish


If your cfcs not related to each other the base.cfc concept does not fit. The inheritance is for classes have common things that can inherit from each other. For example if you have User.cfc and you want to added new cfc called customer.cfc I would inherit from User and override some functionality or add some without touching the actual user.cfc. So, back to your question, since the CFC are not related or have common between each other and to avoid cross referencing, I will create serviceFactory holds instances of cfcs like this

    component name="ServiceFactory"
    {
     function init(){
    return this;
     }

     public User function getUserService(){
    return new User();
     }

     public Calendar function getCalendar(){
    return new Calendar(); 
     } 
     }

and referencing it by

serviceFactory= new ServiceFactory();
userService = serviceFactory.getUserService();

Keep in mind this approach works only if you have sort of another CFC to manage your logic

you can do the same for all other services. If your functions are static you can save your services in application scope and instantiate it only one time (like singleton).

The third option you have is DI(dependency Injection) framework

like image 45
Click Avatar answered Sep 20 '22 16:09

Click