Avg. Rating 5.0
Tags:



Problem

There seems to be a lot of confusion about when and how to properly use the cfinterface tag.

Solution

This solution will take you through a real world example of using the cfinterface tag to make your components more powerful and flexible.

Detailed explanation

One of the more confusing things I encountered while learning OO concepts was the concept of interfaces. Definitions of interfaces were abundant but I couldn't find much information on how they were useful in an actual application.  This recipe will focus on a real world example of using an interface in an application and hopefully will help clarify how they can be used to create a more robust object model.

At a very high level an interface can be treated like another data type, just like a component can be treated like a data type.  Also, just like components interfaces can be declared as an argument type for a function.  However, unlike components, interfaces have no implementation code (code that actually does something) inside of their function definitions.   All an interface does is define a components function names and signatures.  A function's signature is the functions name plus its unique combination of arguments.  Let's take a look at an example of an interface that defines a "Document" object.

interface hint="I represent either a binary or xml document"
{
  public any function GetDocumentData()
  hint="I send back document data" output="false";
}

Looks pretty simple, right?  It probably looks pretty useless as well, but that's OK for now; I'll get into the power of interfaces a little later.  The main thing to note here is that there is no implementation code, the function is just a skeleton.  This interface defines a contract for any component that implements it and says that the component must have a method called GetDocumentData that returns the type of "any".  It's up to the implementing component to actually do stuff within the GetDocumentData function.

For this example a Document object can represent either a Binary document such as a Word or Excel file or an XML document.   Since the handling of binary data is different than the handling of XML data I needed to create separate components to handle each type of document. Here is the code for the  BinaryDocument component:

component displayname="BinaryDocument" implements="IDocument" hint="I represent a binary document (word, excel, etc)" output="false"
{
  variables.documentData = ToBinary('');

  public void function SetDocumentData(binary documentData){
    variables.documentData = arguments.documentData;
  }

  public any function GetDocumentData()
  hint="I send back document data" output=false
  {
    return variables.documentData;
  }
}

Looking at the code you see that the GetDocumentData function now returns variables.documentData which is of a Binary object type and is set by the SetDocumentData function. The XMLDocument component is very similar, except the document data is stored as a string:

component displayname="XMLDocument" implements="IDocument" hint="I represent a XML document (word, excel, etc)" output="false"
{
  variables.documentData = "";

  public void function SetDocumentData(string documentData){
    variables.documentData = arguments.documentData;
  }

  public any function GetDocumentData()
    hint="I send back document data" output=false
  {
    return IsXml(variables.documentData)?XMLParse(variables.documentData):false;
  }
}

This is where interfaces start to become useful.  Lets say that your system also has an Attachment component that has some data that is common amongst both binary and XML documents.  Instead of creating separate Attachment components for each type of document lets use an Interface to represent either type of Document.

The Attachment component should have a Document property which is set using the SetDocument function.  Whats important to see here is that the argument type that the SetDocument function expects is our interface, IDocument.  This means that any component that implements the IDocument interface can be passed into this function.  Since we know that the IDocument interface defines a function called GetDocumentData we can call that function on the Document property to return the data from our Document component.   The code for the Attachment object is shown below (I've removed all the other properties from this function to keep things terse):

component displayname="Attachment" hint="I represent an attachment" output="false"
{
  property name="variables.Document" type="IDocument" setter="true";

  public IDocument function getDocument()
  {
    return Document;
  }

  public void function SetDocument( required IDocument Document )
  {
    variables.Document = arguments.Document;
  }


  public any function GetDocumentData()
  description="I return a documents data as defined by the Document object." output="false"
  {
    return variables.Document.GetDocumentData();
  }

}

The following code is shows how all of these components are put together to output different types of data:

<cfscript>
//create a new binary document

BinaryDocument = new BinaryDocument();

BinaryDocument.SetDocumentData(toBinary(''));

//create a new XML document

XMLDocument = new XMLDocument();

XMLDocument.SetDocumentData('<data>XML is it your friend or foe?</data>');

Attachment = new Attachment();

Attachment.setDocument(XMLDocument);

WriteDump(Attachment.GetDocumentData());

Attachment.setDocument(BinaryDocument);

WriteDump(Attachment.GetDocumentData());

</cfscript>

For additional reading on Interfaces check out Sun's Java Tutorials at:  http://download.oracle.com/javase/tutorial/java/concepts/interface.html.  

I'd also suggest reading Erich Gamma's post on why using interfaces is preferred over inheritance at:  http://www.artima.com/lejava/articles/designprinciples.html.


+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes