Skip to content


Why Interfaces In ColdFusion Are Irrelevant

A post from Sean on Static vs. Dynamic languages reminded me that I needed to get around to finishing up a piece I started long ago about why I think Interfaces in ColdFusion are completely irrelevant. In particular, this quote is what reminded me about it:

I've built large systems in dynamic lanaguages. I've built large systems in static languages too. Neither is necessarily better or worse than the other in terms of maintainability - if you know what you are doing.

To me, a decent but not perfect general comparison of dynamic languages to static languages could be summed-up as a pair of safety scissors vs. a razor blade. Static languages are the safety scissors-- they help protect you from cutting yourself and will probably last longer, but it might take a long time to cut something. Dynamic languages are a razor blade-- they are very sharp and nimble and cut through most things very quickly, but they are easy to cut yourself with if you're not careful and may have a shorter shelf-life depending on how they are used. Granted I don't think this quite applies to any language in which you need to do memory management (C, C++, etc).

Since the introduction of ColdFusion MX, we've been able to use ColdFusion Components-- ColdFusion's take on Object Oriented Programming. CFMX 6.0 was certainly an "interesting" time for CFC's, but luckily a lot of changes were made and CFC's have basically stayed the same since CFMX 6.1. Since that time a lot of ColdFusion developers (including myself) have jumped on the OOP bandwagon (with great community evangelists and teachers like Sean Corfield and Hal Helms, how could you not?) and as we've progressed along we've wanted more and more rigid OOP practices available to us in CFC's. There was a time when I would have liked to have interfaces and would have debated the topic to death in favor of them, though quickly my desires for them dissipated and now I don't really see the point of having them in ColdFusion.

In Java, one of the advantages to using an interface is that it does compile time checking to make sure that classes implementing interfaces follow the contract dictated by the interface. Since ColdFusion is a dynamic language, there really isn't a good way to do compile time checking of interfaces, so it would be a pointless endeavor.

When I find a need for an interface in ColdFusion, I essentially create my own pseudo interfaces / abstract "classes" which at least provide run-time checking in case an inheriting object's method is called. It's not a very good solution, but it does at least offer some sort of contract that an inheriting object can be tied to, mostly for documentation purposes. I've seen several other people do it as well, but it looks something like this:

foobar/Foo.cfc

CODE:
  1. <cfcomponent hint="Interface for foo">
  2.         <cffunction name="init" returntype="any">
  3.                 <return this />
  4.         </cffunction>
  5.  
  6.         <cffunction name="implementThis" returntype="any">
  7.                 <cfthrow message="foobar.Foo:  implementThis() must be implemented">
  8.         </cfthrow></cffunction>
  9. </cfcomponent>

foobar/Bar.cfc

CODE:
  1. <cfcomponent hint="Does foobar">
  2.         <cffunction name="init" returntype="any">
  3.                 <return this />
  4.         </cffunction>
  5.  
  6.         <cffunction name="implementThis" returntype="any">
  7.                 <cfreturn true />
  8.         </cffunction>
  9. </cfcomponent>

These are just poor pseudo-code examples, but you'll get the idea.

Overall, I think the bad reputation that dynamic languages get are because they are so easy to program in so most anyone can do it, while static languages like Java force even bad coders into at least some semblance of decent coding practices. I think it really all comes down to experience, discipline, and knowledge.

In summary, due to the nature of dynamic languages, the implementation of interfaces in ColdFusion would not prevent someone from breaking the contract of an interface before runtime, so I think there are bigger problems to solve in the next version of ColdFusion, currently code named Scorpio.

Posted in A Day In The Life Of, ColdFusion, Culture, Tips, Hacks, & Tricks, Uncategorized. Tagged with , , , , , , .

8 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. "the implementation of interfaces in ColdFusion would not prevent someone from breaking the contract of an interface before runtime"
    I'm still not sure how you got to that conclusion - a proper interface implementation *would* prevent you from not implementing everything you should by throwing a compile-time error (at first run of the CFC).

  2. "In Java, one of the advantages to using an interface is that it does compile time checking to make sure that classes implementing interfaces follow the contract dictated by the interface. Since ColdFusion is a dynamic language, there really isn't a good way to do compile time checking of interfaces, so it would be a pointless endeavor."

    The second part of this statement isn't really accurate. In the BlueDragon 7.0 implementation of CFC interfaces (and abstract CFCs), the check is make at the time a CFC is created using CFOBJECT or CreateObject to make sure it implements the required functions as dictated by the interfaces.

    While not as "early" as a compile-time check, it's still better than not getting a runtime error until you actually try to invoke a method that isn't implemented (such as what happens with "duck typing").

  3. Part of the interface contract in Java is method signatures; you know what data types will be returned, not just that the methods exist. It sounds like BlueDragon just checks to make sure that a given method is implemented? I'm not sure how it would be able to catch data type errors at compile time.. i.e., something returned a string instead of an object.

    Because of the nature of dynamic languages, there is not a way the compiler can check to ensure a given method will return what you expect. Here is a poorly and quickly hacked together example (and I'm just making up some cfinterface syntax as I haven't used BD 7.0) which is coded for brevity, but:

    Fooable.cfc

    CODE:
    1. <cfinterface>
    2.  
    3.     <cffunction name="getInt" returntype="numeric" />
    4.    
    5.     <cffunction name="isRunning" returntype="string" />
    6.    
    7. </cfinterface>

    Foo.cfc

    CODE:
    1. <cfcomponent interface="Fooable">
    2.    
    3.     <cffunction name="init" returntype="any">
    4.         <cfset Variables.Bar = CreateObject("Bar").init() />
    5.         <cfreturn this />
    6.     </cffunction>
    7.  
    8.     <cffunction name="getInt" returntype="numeric" />
    9.         <cfreturn Variables.Bar.getData() />
    10.    
    11.    
    12.     <cffunction name="isRunning" returntype="boolean">
    13.         <cfreturn Variables.Bar.returnSelf().isRunning() />
    14.     </cffunction>
    15.  
    16. </cfcomponent>

    Bar.cfc

    CODE:
    1. <cfcomponent>
    2.  
    3.     <cffunction name="init" returntype="any">
    4.         <cfreturn this />
    5.     </cffunction>
    6.    
    7.     <cffunction name="getData" returntype="numeric" />
    8.         <cfset var randNum = RandRange(1, 4) />
    9.         <cfset retValue = "" />
    10.        
    11.         <cfswitch expression="#randNum#">
    12.             <cfcase value="1">
    13.                 <cfset retValue = "This is not numeric" />
    14.             </cfcase>
    15.             <cfcase value="2">
    16.                 <cfset retValue = StructNew() />
    17.             </cfcase>
    18.             <cfcase value="3">
    19.                 <cfreturn this />
    20.             </cfcase>
    21.             <cfcase value="4">
    22.                 <cfreturn 123 />
    23.             </cfcase>
    24.             <cfcase value="5">
    25.                 <cfreturn true />
    26.             </cfcase>
    27.         </cfswitch>
    28.        
    29.         <cfreturn />
    30.    
    31.    
    32.     <cffunction name="returnSelf" type="any">
    33.         <cfreturn getData() />
    34.     </cffunction>
    35.    
    36.     <cffunction name="isActive" returntype="boolean">
    37.         <cfreturn getData() />
    38.     </cffunction>
    39.    
    40. </cfcomponent>

    There are several obvious problems here that compile time checking of a dynamic language can't check. Because the compiler can't restrict what you use a given variable for, the method getInt() will compile but will throw runtime errors each time it is called because the interface can't ensure that a value of numeric will be returned.

    While you can dictate that methods be defined, there isn't a way to make them follow the contract of what they should return at compile time. This is not the best example, but it's what I can come-up with without detracting myself from work for too long.

    Vince-- what would happen in BD with code similar to this? Also, can you invoke interfaces as an object or do they act the same as interfaces in Java?

  4. Hmm, there are a couple of errors in that code compared to what I typed and I'm not sure why.. must be a problem with the code formatter I'm using. Anyhow, excuse any obvious problems (ie.. missing a cffunction close tag).

  5. You're correct, it's not possible to check the entire method signature when the CFC is created, only the signature name. (By the way, this is consistent with the way it works when you override a CFC super-class method: the override is based only on the method name, not the entire signature as it is in Java.) So it's not perfect, but I wouldn't consider that an argument against interfaces.

    I'm drafting a blog entry entitled, "Always Specify Argument and Return Types, and Why Interfaces Are Good" to address these questions, because I think there are a lot of misperceptions and misinformation out there. Unfortunately, I do have a day job, so completing that blog entry may take a few more days...

  6. I meant to say, "it's not possible to check the entire method signature when the CFC is created, only the method name."

  7. One benefit of having interfaces in CF that no one seems to have touched on is the fact that they would allow us to extend our components with multiple other components, beyond the single inheritance option we currently have. Sure you could use mixins to add multiple behaviors to another component but I feel it's a very non-oo solution to solving that problem. What happens if I wanted to introspect my duck component to see if it's quackable?

  8. @Vince

    To me an interface gets a varying, loose definition when dealing with a dynamically typed language. For instance, I've seen at least a couple of proposals for interfaces in Python (which has been around just a little bit longer than CF) but none of them have ever been approved because there are such a large gaps between different people's interpretations of what an interface should do in a dynamic language, its syntax, etc.

    I can understand the slight boost in productivity in implementing an interface tag vs. the code example I used, but otherwise it just seems like a difference in semantics as the functionality is fairly similar as there isn't a way to always ensure a method is going to accept/return what you expect. I guess it seems more like a template to me? Granted, I haven't looked at how it's implemented in BD 7 at this point.

    @JAlpino

    Good point. I do wish CFC's supported some sort of import statement as other dynamic languages do. It kind of sucks having to have various objects created instead of just including them with objects that you use them in; in particular utility type components.

Some HTML is OK

(required)

(required, but never shared)

or, reply to this post via trackback.