Accessibility
 
Home / Developer Center / ColdFusion MX Application Developer Center /

ColdFusion Article

Icon or Spacer Icon or Spacer Icon or Spacer
Hal Helms
Hal Helms

www.halhelms.com

Using inheritance and composition in ColdFusion components


ColdFusion components (CFCs) borrow features from object-oriented languages such as Smalltalk and Java. ColdFusion developers can benefit from understanding the different ways objects relate to one another and how to use these relationships to model real-world situations. In this article I discuss the two most prominent object relationships: inheritance and composition.

There is a technical difference between composition and aggregation having to do with whether the helper object has an independent existence from the main one. In this article, I use "composition" to encompass both aggregation and composition.

 

For many developers, inheritance is the most striking feature of CFCs. Inheritance allows you to model relationships in which one object is a specialized type of another.

Examples of this relationship abound in the real world. For instance, a "SportsCar" is a specialized type of "Car" which, in turn, is a specialized type of "Vehicle."

The specialized object is known as a subtype, while the more generalized object is called a supertype. An object can be both: Car is a supertype to SportsCar and also a subtype to Vehicle.
One of the benefits of inheritance is code reuse. Code that performs actions for a supertype can be used by the subtype—and by any sub-subtypes (because an inheritance tree can encompass specializations of specializations).

Relating objects with inheritance
You can model supertype–subtype relationships visually using a UML (Unified Modeling Language) class diagram, as shown here. The arrow points from the subtype to the supertype:

 
Figure 1:  Supertype and subtype UML Diagram
 

The operative phrase describing these relationships is "is a." For instance, a SportsCar is a Car and a Car is a Vehicle. "Is a" relationships are very simple to implement in CFCs by pointing the extends property to the supertype. This code will create a Car component that inherits both properties and methods from the Vehicle component:

<cfcomponent hint="I am a Car" extends="Vehicle">
   <cfset this.locomotion = "internal combustion engine">
   <cfset this.speedIncrement = 20>
   
   <cffunction name="stepOnGas" output="yes">
      <cfset currentSpeed = this.increaseSpeed( this.speedIncrement )>
      You are now going #currentSpeed# <br>
   </cffunction>
   
   <cffunction name="stepOnBrakes" output="Yes">
      <cfset currentSpeed = this.decreaseSpeed( this.speedIncrement )>
      You are now going #currentSpeed# <br>
   </cffunction>
   
   <cffunction name="stop" output="Yes">
      <cfloop condition="#this.mph# GT 0">
         <cfset this.stepOnBrakes()>
      </cfloop>
   </cffunction>
</cfcomponent>

For the Vehicle component, the code might look like this:

<cfcomponent hint="I am a Vehicle">
   <cfset this.speedIncrement = 0>
   <cfset this.mph = 0> 
   
   <cffunction name="increaseSpeed" returntype="numeric">
      <cfargument name="mph" type="numeric" required="yes">
      <cfset this.mph = this.mph + arguments.mph>
      <cfreturn this.mph>
   </cffunction>
   
   <cffunction name="decreaseSpeed">
      <cfargument name="mph" type="numeric" required="yes">
      <cfif this.mph GTE this.speedIncrement>
         <cfset this.mph = this.mph - arguments.mph>
         <cfreturn this.mph>
      </cfif>
         <cfset this.mph = 0>
         <cfreturn this.mph>
   </cffunction>
   
   <cffunction name="stop" output="Yes">
      <cfset this.mph = 0> 
      Your vehicle has stopped.<br>
   </cffunction>
</cfcomponent>

Notice that Car.cfc is making method calls to itself (the this variable is self-referential) even though they actually belong to Vehicle.cfc. This is the essence of inheritance—the subtype (Car) can use of the methods of the supertype (Vehicle).

Note, too, that Car and Vehicle each define a method called stop(), although each implements the method differently. When subtypes or supertypes have identically named methods, the code in the subtype takes precedence over the code in the supertype. In other words, the subtype's method is said to override the supertype's methods.

Let's test the code for Car and Vehicle. Create a CFML file called TestCar.cfm, making a Car object:

<cfset myCar = CreateObject( 'component', 'Car' )>

Now try your new car out by writing TestCar.cfm:

<cfset myCar.stepOnGas()>
<cfset myCar.stepOnGas()>
<cfset myCar.stepOnGas()>
<cfset myCar.stepOnGas()>

When you browse TestCar.cfm, you'll see the following output from the Car object:

 
Figure 2:  Car object output
 

Whoa! Better slow down. Add this to the bottom of TestCar.cfm:

<cfset myCar.stepOnBrakes()>

Execute the page again:

 
Figure 3: Car object output after revision
 

Even though the methods that perform acceleration and deceleration—increaseSpeed() and decreaseSpeed()—belong to Vehicle.cfc, Car.cfc inherited them because it's a subtype of Vehicle.

Note also that when myCar.stop() is called, the stop() method of Car.cfc will execute, not the one in Vehicle.cfc. This is an example of method overriding.

Place the following code at the bottom of TestCar.cfm and execute it again:

<cfset myCar.stop()>

Here's what you see after the code calls the stop() function:

 
Figure 4: Car object with stop() function added
 

Relating objects with composition
Inheritance (also called a "specialization relationship") is very powerful, yet the "is a" relationship is only one way that objects can relate to each other. Usually, one object has another object for one of its properties. In this relationship, the operative phrase is "has a," as in a Person has an Address. Such relationships between objects are called composition.

In the case of the Person object, without using composition, you might model it as follows:

<cfcomponent hint="I am a Person">
   <cfset this.firstName = "">
   <cfset this.lastName= "">
   <cfset this.address1 = "">
   <cfset this.address2 = "">
   <cfset this.city = "">
   <cfset this.province = "">
   <cfset this.postalCode = "">
</cfcomponent>

Of course, you have to write methods for retrieving and setting individual variables—getters and setters, as they're called. This is not difficult, however.

Below is a sample getter and setter for a single property, address1:

<cffunction name="getAddress1" returntype="string">
   <cfreturn this.address1>
</cffunction>

<cffunction name="setAddress1">
   <cfargument name="address" required="yes" type="string">
   <cfset this.address1 = arguments.address>
</cffunction>

<!--- more getters and setters below --->

Create getters and setters for all the properties of Person. Note that you now have another CFC called Venue:

<cfcomponent hint="I am a Venue">
   <cfset this.venueName = "">
   <cfset this.costPerDay= 0>
   <cfset this.address1 = "">
   <cfset this.address2 = "">
   <cfset this.city = "">
   <cfset this.province = "">
   <cfset this.postalCode = "">
</cfcomponent>

Hmmm…Venue, too, needs address information. This means that you'll end up writing identical getter and setter code that uses the addresses properties in both Venue and Person. A better method would be to create a separate CFC called Address and relate that to Person by means of composition. The following UML class diagram shows the relationship:

 
Figure 5:  UML diagram for composition
 

The Address.cfc is simple and does not do much:

<cfcomponent hint="I am an Address">
   <cfset this.address1 = "">
   <cfset this.address2 = "">
   <cfset this.city = "">
   <cfset this.province = "">
   <cfset this.postalCode = "">

   <!--- add getters and setters below --->
   <cffunction name="new">
      <cfargument name="add1" type="string">
      <cfargument name="add2" type="string">
      <cfargument name="city" type="string">
      <cfargument name="province" type="string">
      <cfargument name="postalCode" type="string">
   
      <cfset this.address1 = arguments.add1>
      <cfset this.address2 = arguments.add2>
      <cfset this.city = arguments.city>
      <cfset this.province = arguments.province>
      <cfset this.postalCode = arguments.postalCode>
   </cffunction>
</cfcomponent>

Now when Person—or any CFC—needs an address, it doesn't have to keep each piece of address information as a separate property. Instead, a separate Address object can do this by calling the new() method of Address (shown above). Then, you can set the resulting object as a property of Person.

Rather than show you a TestPerson page, let's start with a form that a user might see to show how to incorporate objects into an application:

 
Figure 6: testperson.cfm form page
 

The code for TestPerson.cfm is as follows (Note that the image above formats the form using HTML table tags. I did not include the HTML table tags in the code below, however, as it was my goal to show you the form variable names.):

<cfform action="processuserform.cfm" method="post">

	First Name: <input type="text" name="firstname"> <BR>
	Last Name: <input type="text" name="lastname"><BR>
	Address 1: <input type="text" name="add1"><BR>
	Address 2:  <input type="text" name="add2"><BR>
	City:  <input type="text" name="city"><BR>
	State/Province:  <input type="text" name="province"><BR>
	Zip/Postal Code:  <input type="text" name="postalcode"><BR>
	<input type="submit" value="ok">

</cfform>
 

The form's action property has been set to ProcessUserForm.cfm, but before looking at that code, let's rework Person.cfc:

<cfcomponent hint="I am a Person">

   <cfset this.firstName = "">
   <cfset this.lastName = "">
   <cfset this.address = "">

   <cffunction name="new">
    <cfargument name="firstName" type="string">
    <cfargument name="lastName" type="string">
    <cfargument name="address" type="Address">

    <cfset this.firstName = arguments.firstName>
    <cfset this.lastName = arguments.lastName>
    <cfset this.address = arguments.address>
  </cffunction>

</cfcomponent>

Note that in the cfargument tag for address, the type property is set to Address. This informs ColdFusion to expect an Address object as the argument.

With both Address and Person set up, there's not much to the ProcessUserForm.cfm code:

<!--- ProcessUserForm.cfm --->

<cfset add = CreateObject( 'component', 'Address' )>
<cfset add.new( form.add1, form.add2, form.city,
form.province, form.postalCode )>

<cfset user = CreateObject( 'component', 'Person' )>
<cfset user.new( form.firstName, form.lastName, add )>

<cfdump var="#user#">

When you fill out the form and submit it, you'll see the following cfdump output:

 
Figure 7:  cfdump output
 

As you can see, ColdFusion components are an intriguing addition to ColdFusion. They provide a powerful means of code reuse and encapsulation. By using inheritance, developers can write code in supertypes and allow subtypes to borrow the code. As new requirements require new components, the cost of writing these components can be quite small. Inheritance works well when the relationship between components is that between a parent and a child.

There are a many situations where objects are related where the parent-child metaphor is not appropriate. These are ideal situations for you to use composition. Developers can reuse code while keeping components small and well-defined—the necessary conditions for code reuse, and a great aid to those who maintain existing code.

Understanding how objects can relate to each other is key to making the greatest use of the new features of ColdFusion components. Master inheritance and composition and you'll be well on your way to building more reusable and maintainable code.

 


About the author
Hal Helms is a Team Macromedia member, well-known trainer, writer, and speaker on software development and project management. His latest book is Discovering CFCs: ColdFusion MX Components, available at www.techspedition.com.

He teaches, writes, speaks, and consults on web development using the Fusebox methodology. He offers scheduled classes and onsite training. He is also the author of the upcoming book, ColdFusion to Go (Prentice Hall), and is currently working on two other books. His work centers on successful team development approaches to development, particularly with large-scale projects.

He began his career with Smalltalk and Java before moving wholeheartedly to ColdFusion. He has architected large "webplications," including the e-commerce site for the United States' largest furniture retailer, the Latin American entertainment portal for Fox Entertainment, and the commercial software product RapidTeam.