Abstract Class as an Interface

October 06, 2008

I prefer to use Abstract classes instead of an interface. The downside of using Abstract Classes is that you can't enforce that a method must exist in each subclass, or so I thought! I just spotted this handy technique, that Matt Woodward, uses for forcing overriding in subclasses (and pretty much removing the need for using an interface in ColdFusion).

The Abstract Class

<cfcomponent displayname="aShape" 
  output="false"
  hint="I am an abstract class and should not be initiated directly only extended.">

  <cffunction name="draw" access="public" output="true" returntype="void">
    <cfthrow type="Application" 
      message="Method Must Be Overridden" 
      detail="The draw method must be overridden in a specific shape object." />
  </cffunction>
  
</cfcomponent>

By doing this it means that if you try to call the draw method of a subclass (without overriding it in the subclass) then it will throw an error. You then know that you must override the method in your subclass. It's really simple, and as we all know - the best ideas usually are (so why didn't I think of it!)


4 comments

  1. Since Matt is a member of the Open BlueDragon steering committee (www.openbluedragon.org/about.cfm), it might be appropriate to point out that BlueDragon offers an alternative solutions to this problem. In addition to supporting CFC interfaces, BD also supports abstract CFCs and abstract functions. So you could rewrite the code like this (note the use of type="abstract" for both the cfcomponent and cffunction tags):

    <cfcomponent displayname="aShape" type="abstract">
    <cffunction name="draw" access="public" output="true" returntype="void" type="abstract" />
    </cfcomponent>

    The advantages of this approach are: (1) BD won't let you directly create an instance of "aShape" due to the type="abstract" attribute of the cfcomponent tag; and, (2) BD will give you a runtime error immediately when you create an instance of a subclass of "aShape" that doesn't implement the "draw" function.

    The second point is particularly important. Matt's solution (as you present it) doesn't force you to override the "draw" function unless it's actually invoked by your code. So unless you have a testcase that invokes the "draw" function, you could have a latent bug in your code that may rear its head at an unfortunate time. (OK, so a "draw" function in this example is probably obviously going to be tested, but you can see where a less used or more obscure function might not be). The advantage of the BD implementation is that it really does force you to override the "draw" function in order to create a subclass instance.

    Comment by Vince Bonfanti – October 06, 2008
  2. Hi Vince, Thanks! Really interesting comment and you make a good point about the method only being enforced if you call it. The abstract attribute is a nice solution to the problem. It's true that the code I posted could potentially hide a future problem, although this could be trapped with some good unit tests.

    Comment by John Whish – October 06, 2008
  3. Why do you prefer the wrong tool ? CF has a perfectly good CFINTERFACE tag, why not use it when you are, you know, *defining an interface* ?

    Comment by Tom Chiverton – October 07, 2008
  4. Hi Tom, I'm no expert but it seems to me that there are times when it makes perfect sense to use an abstract class which acts as an interface.
    Using interfaces is great for forcing implementation when the class is initiated, which the abstract class won't.
    The abstract class as an interface way allows you to enforce implementation and also structure, and you can use both together if you want.
    As to when to use an interface or an abstract class, I'm afraid that people much cleverer than me still argue over that one! :)

    Comment by John Whish – October 07, 2008

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.