ColdFusion 8 added the cfinterface tag, which enables CF developers to use interfaces in their applications. Interfaces specify one or more functions that a ColdFusion component must implement. Coming from being a Java programmer to primarily doing ColdFusion web application development, I missed using interfaces. To help me learn how to use the new cfinterface tag, I implemented the SimUDuck application from chapter 1 of Head First Design Patterns. You can download the code (see the references below) for the application. I'll briefly walk through how cfinterface works.
Please note, don't comment about whether or not interfaces are good to use in ColdFusion. That debate has been done before on other blogs. I regard the cfinterface tag as just another tool CF provides that I may need to use in designing my web applications. Many design patterns use interfaces and having the cfinterface tag available can make implementing those design patterns in CF simpler.
- Head First Design Patterns, Eric Freeman and Elisabeth Freeman, O'Reilly
- Adobe CF 8, CFML Reference
- Adobe CF 8, ColdFusion Developer's Guide
- SimUDuck CF 8 Application Code
The SimUDuck application is used in the Head First Design Patterns book to illustrate the importance of three design principles:
- Identify the aspects of your application that vary and separate them from what stays the same
- Program to an interface, not an implementation
- Favor composition over inheritance
Briefly, the SimUDuck application includes many different types of ducks. Some ducks fly, some ducks don't fly. New duck types may be introduced in the future and the fly behavior for current and future duck types is likely to change. (Note the SimUDuck application in the Head First book also discusses a quack behavior, but I don't implement that behavior in my CF example code.)
Since the fly behavior can be different for different duck types and since the fly behavior may change, the SimUDuck example application creates a FlyBehavior interface. This interface merely specifies one function (or method) named fly. A function in an interface doesn't specify the details of how the function actually works (there is no function body).
In CF 8, you use the cfinterface tag to create an interface. Below is the code I used to create the FlyBehavior interface. This code is saved in a file named FlyBehavior.cfc
<cffunction name="fly" displayname="fly"
hint="CFCs that implement the Fly interface must define a fly method"
access="public" output="false" returntype="String" />
Note that the fly function has no body. If the function has any arguments you would include those arguments (see CFML reference for an example of an interface where the function has arguments) in the function body.
To use an interface in CF 8, your CFC provides the interface file name as the value of the new implements attribute of the cfcomponent tag. For example, the code below specifies that the FlyWithWings CFC implements the FlyBehavior interface.
<cfcomponent implements="FlyBehavior" displayname="FlyWithWings"
hint="Encapsulates a specific type of fly behavior" output="false">
<cffunction name="fly" displayname="fly"
hint="Defines the fly behavior specified by the FlyBehavior interface"
access="public" output="false" returntype="String">
<cfreturn "I'm flying with wings"/>
Because the above CFC specifies that it implements the FlyBehavior interface it must define a function named fly. Note that the fly function has a body (it actually performs a specific behavior).
When using interfaces it's important to remember that an interface is a type. So my interface FlyBehavior is a specific type that I can use in my application. For example, in the Duck.cfc I have a setFlyBehavior method that takes an argument of type FlyBehavior.
<cffunction name="setFlyBehavior" access="public" output="false"
hint="Any CFC that implements the FlyBehavior interface can be used as the argument value">
<cfargument name="flyBehavior" type="FlyBehavior" required="true" />
<cfset variables.flyBehavior = arguments.flyBehavior />
Any CFC that implements the FlyBehavior interface is also of type FlyBehavior. Since my FlyWithWings implements the FlyBehavior interface, I can use a FlyWithWings object as the argument to the setFlyBehavior function. I also have a FlyRocketPowered CFC that implements the FlyBehavior interface. So I could also use a FlyRocketPowered object as the argument to the setFlyBehavior function.
Interfaces (like inheritance) enable polymorphism. The code below from the Duck CFC (the parent CFC of all specific Duck types like MallardDuck and DecoyDuck) calls the fly function of the variables.flyBehavior object (whose type is FlyBehavior).
<cffunction name="performFly" hint="Calls the fly method of variables.flyBehavior"
displayName="flyBehavior" output="false" access="public"
<cfreturn #variables.flyBehavior.fly()# />
The actual code (behavior) executed by the call to the fly function depends on which specific FlyBehavior type object (FlyWithWings or FlyRocketPowered for example) was used to set the value of the flyBehavior object at runtime. If the variables.flyBehavior's value was set using a FlyWithWings object (this is legal since FlyWithWings has type FlyBehavior since it implements the FlyBehavior interface), then the fly function from the FlyWithWings CFC will be executed.
Another advantage of using interfaces and encapsulating behaviors that change, is that we can easily change those behaviors at runtime. In the MiniDuckSimulator.cfm demonstration page, I initially create a DecoyDuck type. The DecoyDuck type's fly behavior is of type FlyNoWay (decoy's don't typically fly). But I can easily change the fly behavior of the DecoyDuck object I created by calling its setFlyBehavior method and passing it a new FlyBehavior type object (remember any CFC that implements the FlyBehavior interface will be of type FlyBehavior). So I can create an object of type FlyRocketPowered and pass that object to the setFlyBehavior function of my DecoyDuck and now my DecoyDuck is "flying with a rocket."
Where To Go Next
Download the SimUDuck CF application code to view an example of how to use the CF 8 cfinterface tag. The MiniDuckSimulator.cfm page shows how I used the CFCs and changed an object's fly behavior at runtime.
Read through the explanation of the cfinterface tag in the CFML Reference. Unfortunately, there aren't yet any good examples or explanations about the cfinterface tag and using interfaces in the ColdFusion 8 Developer's Guide.
Lastly, if you're trying to wrap your head around object-oriented programming, I recommend two books: Head First Object-Oriented Analysis and Design and Head First Design Patterns. The code examples in both books are written in Java, but they can easily be converted to CF code examples. The new CF 8 cfinterface tag makes implementing these examples and design patterns in ColdFusion even easier!