Monday, January 24, 2005

Overloading ColdFusion Constructors

If you've been holding your breath, waiting for overloaded methods in ColdFusion, stop. They will probably never be there. You'll wind up with a bad headache and a bluish complexion. Since the process that generally makes overloading possible is due to the language being compiled, cf would have to determine which function you were referencing each time it was interpreted. It would add a lot of overhead to the engine for a feature that has a workaround and is probably not in that great of demand.

If you're not familiar with method overloading, the concept is pretty straight forward. In languages that support overloading (like java), you can have two functions with the same name as long as they have a different collection of attributes.

function myFunction() {
do something here;
}

function myFunction(arg1, arg2){
do something completely different;
}


This won't work in ColdFusion. Each component can only have a single function with the same name regardless of the number and type of arguments passed in. Normally, this isn't that big an issue. Just give the separate functions different names and the problem pretty much goes away. There is one situation I run into almost everyday, however that benefits from an overloading workaround.

I frequently want to overload ColdFusion constructor functions. In many cases, you want to be able to create your objects in one of two ways. In some cases you want a new object that has all of it's attributes initialized to null/empty string/0. In other cases you want to create a new instance of your object and populate its attributes from your database.

As a rule, I don't use the implicit constructor technique that some people use to set up default values for their objects. They do this by putting initialization code in their component outside any cffunction tags. The code gets executed when the object is first created. The drawback is that there is no way to pass values to the newly created object. You can't easily create a new instance of your object with it's attributes populated using the implicit constructor.

Common practice now is to use a function called init() to create new instances of your objects. I actually call mine _init() since it makes the function name float to the top of the method list in Dreamweaver.

In most cases, I give my _init() function a single, optional argument that corresponds to the key used to pull the data from the database. Use a simple switch to check for the existance of the key and you have the basics of an overloaded constructor.

Here's an example:
<cfcomponent displayname="myComponent">
<!--- ==========================================================================
DECLARATIONS
=========================================================================== --->
<cfproperty name="property1" type="string">
<cfproperty name="property2" type="string">

<!--- ==========================================================================
CONSTRUCTOR(S)
=========================================================================== --->
<cffunction name="_init" access="public" returntype="myComponent">
<cfargument name="property1" type="string" required="false">
<cfscript>
variables.property2 = "";


if (isDefined("arguments.property1"))
{
_load(arguments.property1);
}
</cfscript>
<cfreturn this>
</cffunction>
<!--- ==========================================================================
DATABASE METHODS
=========================================================================== --->
<cffunction name="_load" access="private" output="false" returntype="void">
<cfargument name="property1" required="yes" type="string">
<cfquery name="qMyQuery" datasource="#application.ds#">
SELECT * FROM myTable
WHERE property1 = '#arguments.property1#'
</cfquery>
</cffunction>


<!--- ==========================================================================
OPERATIONAL METHODS
=========================================================================== --->


<!--- ==========================================================================
GETTERS
=========================================================================== --->
<cffunction name="getProperty1" access="public" output="false" returntype="string">
<cfreturn property1>
</cffunction>
<cffunction name="getProperty2" access="public" output="false" returntype="string">
<cfreturn property2>
</cffunction>


<!--- ==========================================================================
SETTERS
=========================================================================== --->


</cfcomponent>


4 comments:

  1. "Since the process that generally makes overloading possible is due to the language being compiled, cf would have to determine which function you were referencing each time it was interpreted."

    But CFMX does get compiled. It was CF5- that was an interpreted language. I thought the reason it doesn't allow overloading was because it's a loosely typed language.

    ReplyDelete
  2. I don't really follow how you got around the lack of overloaded methods with a database call... I think most people (in the case of business objects) have an init function like this:

    <cffunction name="init" ... >
    <cfargument name="initialData" type="struct" required="false" default="#structnew()#"/>

    <cfset variables.instanceData = arguments.initalData/>

    <cfreturn this/>
    </cffunction>

    This allows you to populate any property of the CFC during init(), or call it with no arguments and then use setters to populate the properties (which is typically what I do).

    I think the ability to have optional arguments makes up for the inability to overload method signatures. I'd like to see operator overloading though... like:

    <cffunction operator="output">
    <cfoutput>
    <... spill this cfc's guts .../>
    <cfoutput>
    </cffunction>

    and then you could do:

    <cfoutput>#cfcInstance#</cfoutput>

    or

    <cffunction operator="gte">
    <cfargument name="rightSide" type="same.type.as.cfc.we.are.in"/>

    <cfreturn (whether "this" is greater or equal to arguments.rightside) />

    </cffuntion>

    Of couse you can just expose the functionality with your own methods, but it would be cool to say:

    <cfif CFCInstance1 GTE CFCInstance2>
    >cf_dowhatever/>
    </cfif>

    Lastly, of course, if we could have any additions to CFCs, I would bet most would ask for interfaces. NULL support would be awesome too! :)

    -Dave Ross

    ReplyDelete
  3. Anonymous #1: You are correct. CFMX does eventually get compiled into java. It is also the loose variable typing that makes creating function signatures that would support overloading possible.

    When I mentioned compiling, I was talking about what I normally think of as the compiling process. One where I invoke the compiler manually. It is during that process that strongly typed languages generally detect type mismatches.

    Since cf in its current state is compiled when a page is requested for the first time in production, adding the kind of overhead that would be needed to do variable type mismatch detection across your application is impractical. You also wouldn't want to throw an error at that point either.

    I think Blackstone may have the ability to distribute pre-compiled applications. If that is the case, we may be seing the beginnings of a movement to strongly type the language. As I said before, I wouldn't hold my breath.

    ReplyDelete
  4. Right On Dave,

    Interfaces and null support would be terriffic. Unfortunatly, I don't think either one of these made it into cfmx7. There were a lot of people looking for these as part of the release. There was also a pretty protracted discussion from the developers about why they weren't going to do it. I can't for the life of me remember what the reason was. Could it have been the weak variable typing?

    As for my example, I'm just using an if statement to switch between what could have been a set of overleaded constructors. The db call is really not part of the solution. You could have any code in there that you need. I just tend to run into this pattern frequently where I want to fetch an object that has been stored in the db previously.

    After looking at the code, I could have changed it to make that more clear by not rolling through the empty value settup before fetching the values from the db.

    I think we're pretty much on the same page with the use of optional arguments (too bad that doesn't work with web services, but that's another topic).

    ReplyDelete