ColdFusion Components (CFCs) are object-like constructs that became available to ColdFusion developers with the release of ColdFusion MX. While they aren't quite "true" objects, they do have many of the capabilities that objects in other languages have. One of these capabilities is polymorphism.
Polymorphism means "many forms" in Greek. When used with CFCs, polymorphism means that you can call the same method on different types of CFCs, and each one will handle the method call in its own way. It also means that you can pass different types of CFCs as arguments to a method, and let the method respond based on the kind of CFC you pass in.
Things like polymorphism are much more easily demonstrated than explained. So let's set up some CFCs so you can see how useful polymorphism can be.
Creating a CFC wild kingdom
To demonstrate CFC polymorphism, I'm going to create a hierarchy of CFCs that represent animals. At the highest level is an Animal CFC. This is an abstract CFC that encapsulates methods and data that are common to any kind of animal. This includes the animal's size and type. It also defines abstract methods that must be overridden by any subclass of Animal, for methods like eat() and makeNoise().
The Mammal CFC is a subclass of Animal. Just like Animal, the purpose of the Mammal CFC is to wrap up data and methods common to all Mammals. In this example, Mammal doesn't do much, but you can imagine it handling methods like growHair(). Mammal is also an abstract CFC, like Animal. You never create instances of abstract CFCs, but you do create instances of the concrete subtypes of abstract CFCs. For example, a Mammal doesn't actually exist in the real world, a Mammal is a generalization. In the real world there are types of Mammals, like a Zebra or a Mouse. ColdFusion doesn't specifically allow you to designate a CFC as abstract, but you can still develop around the concept.
Next, I've created CFCs that extend Mammal: a Mouse, a Zebra, and a Feline. Mouse and Zebra are concrete CFCs, which means you can create instances of these CFCs. In true polymorphic fashion, Mouse and Zebra both handle the methods eat() and makeNoise() in their own way. This clearly makes sense: when they make noise, a Mouse squeaks and a Zebra barks (yes, Zebras actually bark, they don't neigh).
Feline is another abstract class which could encapsulate methods that apply to all felines, like purr(). And finally, the concrete subtypes of Feline are Tiger and Housecat. And again, Tiger and Housecat have their own way of handling method calls like eat() and makeNoise(). Since a picture is worth a thousand words, you can see a simple class diagram that represents this hierarchy of CFCs in Figure A.
And the code for creating these CFCs is contained in Listing A.
Polymorphism in Action
OK, enough setup. Let's show off polymorphism in action. To start with, we can create instances of all four concrete CFCs in our hierarchy:
Creating a Housecat...
<cfset aHousecat = createObject( 'component', 'housecat' ).init() />
Creating a Tiger...
<cfset aTiger = createObject( 'component', 'tiger' ).init() />
Creating a Mouse...
<cfset aMouse = createObject( 'component', 'mouse' ).init() />
Creating a Zebra...
<cfset aZebra = createObject( 'component', 'zebra' ).init() />
Now that we have instances of all four CFCs, we can call the makeNoise() method on each one and see what happens:
Make some noise, Housecat: #aHousecat.makeNoise()#
Make some noise, Tiger: #aTiger.makeNoise()#
Make some noise, Mouse: #aMouse.makeNoise()#
Make some noise, Zebra: #aZebra.makeNoise()#
We get back a cacophony of sounds: a meow, a roar, a squeak and a bark. Since our abstract CFC Animal requires that its subtypes override the makeNoise() method, each kind of animal responds to makeNoise() in its own way. Polymorphism in action! This way, you know that even if you create 50 different subtypes of Animal, you can call makeNoise() on every one of them and get the appropriate response. We can see this again by calling eat() on the Mouse and the Zebra:
A Mouse eating: #aMouse.eat()#
A Zebra eating: #aZebra.eat()#
The responses are "I like cheese" and "Yummy grass", respectively.
Let's look at one more twist on polymorphism. The eat() method is handled a bit differently by the Housecat and the Tiger. For these, we have to pass an Animal into the eat() method for the Housecat or Tiger to eat, like this:
<cfargument name="prey" type="animal" />
This means that the eat() method will take an animal, or any subtype of animal, as a valid argument. So it's legal for me to pass a Zebra to the eat() method. A Mouse is fair game as well (pun intended). But if I try to pass a Car CFC instance to the eat() method, we're going to get some nasty errors (as we should, you definitely don't want to see a cat try to eat a car). Let's try feeding our Housecat:
Hey Housecat, eat the Zebra: #aHousecat.eat( aZebra )#
While Zebra is a valid type of Animal, it's too big for our poor Housecat to feast on, so the cat responds with "Are you crazy? I can't eat that!" Let's try something a little more appropriate:
Hey Housecat, eat the Mouse: #aHousecat.eat( aMouse )#
We get a more satisfying (for the cat anyway) "Crunch...gulp. Mmmmmm."
Polymorphism is a very powerful and useful element of object-oriented languages. Now, through the use of CFCs, ColdFusion developers can leverage this power. Polymorphism can be a bit confusing to grasp when you first start exploring it. But with a little practice and experience, it will quickly become a handy programming tool. I hope these examples have given you some ideas on how you can use polymorphism in your own applications.