The Mystery of Polymorphism
I started my long, bumpy journey into OO land sometime back in 2003. I read a few really good introductory books back then about object orientation, recommended by various people on the lists.
Here's what i read at that time:
1) Discovering CFCs - Hal Helms, Ben Edwards
2) Design Patterns Explained - Alan Shalloway, James Trott
3) The Object Primer - Scott Ambler
4) Object Technology - David Taylor
Now you might want a quick review of those books, so here goes.
Discovering CFCs is actually very good in it's core content, but because the way CFCs are implemented has changed since CFMX 6, the book is now inaccurate on some key implementation points concerning CFCs. So it's hard for a newbie to distinguish what's true and what isn't.
Design Patterns Explained is simply an excellent book, and of all of these introductory books, it's the one i recommend the most. It walks you through the thought process involved in developing an application design and is very well written. The Object Primer is also a very good book if you can find a copy, describing in detail some effective, simple techniques you can use to design a model. Object Technology is a generalized overview of OO, suitable for non-technical people.
Here's a quote from Hal's book that remained a mystery to me for a surprisingly long time.
"Encapsulation and inheritance are two of the three horses that drive the object oriented troika. The last is polymorphism."
Inheritance and encapsulation aren't too difficult to understand. But the troika quote gave a sort of mysterious significance to polymorphism. I noticed that same significance in other articles i read online Those are the big 3 OO concepts that are mentioned again and again. On the surface, like all OO concepts, it's simple. Polymorphism means that something can take many forms.
OK. So what i wanted to know was this. What can i, a fledging, self-trained CF developer, do with polymorphism? What good is it?
To answer this question, i needed to figure out a few things first. It took me awhile, because i didn't know what i needed to figure out ahead of time. So it happened in kind of a random, haphazard way.
1) I needed to understand the syntax used in ColdFusion components. Not too difficult, but still ... that was the very first step. It takes a little time to become fluent in it.
2) I needed to understand what typing is, and how typing works in ColdFusion. Practically speaking, the "type" is what you put in either the type attribute of an argument tag or the returntype attribute of a function tag. Since i had never been exposed to this "typing" thing before in the context of objects, it was completely new to me.
3) I needed to understand how inheritance works in ColdFusion. ... Practically speaking, inheritance kicks into gear when you a) use the extends attribute in the cfcomponent tag and b) instantiate the subtype (child) component - the one with the extends attribute.
4) I needed to understand the difference between statically typed and dynamically typed languages.
5) I needed to understand that object orientation is most widely used in statically typed languages, and that this gives a certain context to the literature that is available on the topic.
I also needed to read a lot of posts on the internet.
Let's take a deeper look into each of these, beginning with typing. (I'll let you research the syntax of components and functions on your own if you haven't already. Google for cfcomponent and cffunction, you'll easily find the URL to Macromedia's livedocs page that way.)
When i first encountered the word "type" in the context of OO, i wasn't exactly sure what it meant. Let me explain by walking you through some practical examples. In ColdFusion, you can specify the type in one of 2 places. One is in the returntype of a function, like so:
<cfreturn variables.Person />
</cffunction>
What this means is that the variable specified in the cfreturn tag, which in this case is variables.Person, has to be of the type Person. If it's not, then ColdFusion will throw an error at runtime saying "The value returned from function getPerson() is not of type Person.".
By the way, "at runtime" means when the code is run. Some languages, like Java, are precompiled, and they would throw a compile-time error when you attempt to compile your code if your typing is incorrect, and you'd never get the chance to run it.
Hence the distinction between runtime and compile-time. ColdFusion will run code that isn't typed properly. Java won't.
And we haven't exactly defined what type is yet. Hang on. We'll get there.
The other place you can specify type is in the type attribute of the argument tag. Here's what that looks like:
<cfargument name="Person" type="Person" />
<cfset variables.Person = arguments.Person />
</cffunction>
What this means is that the argument called Person being passed into this function setPerson() has to be of the type "Person". If it's not, then ColdFusion will throw an error at runtime saying "The argument PERSON passed to function setPerson() is not of type Person.".
You might notice that this function has a returntype of void, which means that it is not allowed to return any variable to the calling code. If you try to return a value from a function with the returntype specified as void, then you'll get a runtime error. "The function functionName defined as void tried to return a value. Void functions must not return any values."
Now that we've seen some code, let's see if we can define "type". The documentation for CFMX7 http://livedocs.macromedia.com/coldfusion/7/htmldocs/00000263.htm specifies that any of these are valid values for either the type or returntype attributes.
any
array
binary
boolean
date
guid
numeric
query
string
struct
uuid
variableName: a string formatted according to ColdFusion variable naming conventions.
void: does not return a value
xml: allows web service functions to return CFML XML objects and XML strings.
a component name: if the type attribute value is not one of the preceding items, ColdFusion treats it as the name of a ColdFusion component. When the function executes, it generates an error if the argument that is passed in is not a CFC with the specified name.
As you can see, you can use native types, like array, numeric, string, struct, a variable name, or you can use the name of a CFC. ColdFusion assumes it's a CFC if it can't match it to any other type.
For our discussion of type in an OO sense, we're only concerned with this last case, which the documentation calls "a component name".
The first thing to understand here is that if the calling code is in the same directory as the CFC in question, you can simply use the name of the CFC as the type. Let's say that the code below is in a CFC called Person:
<component>
...
<cffunction name="setAddress" returntype="void">
<cfargument name="Address" type="Address" />
<cfset variables.Address = arguments.Address />
</cffunction>
</component>
For typing to work in this case, where only the name of the CFC is specified in the type attribute, Address.cfc would need to be in the same directory as Person.cfc. ( Strictly speaking, Address.cfc could also be in the webroot of the site, but that's generally not a very good way to organize your code).
Now, as soon as these CFCs would be in different directories, then you would need to specify the full dot path to the CFC as the type for typing to work.
To give an example, let's say that the path, from the webroot, to Person.cfc is org\model\Person.cfc and the path, again from the webroot, to Address.cfc is components\Address.cfc. Then you'd have to specify Address's type as the full dot delimited path to the CFC, like so:
<component>
...
<cffunction name="setAddress" returntype="void">
<cfargument name="Address" type="components.Address" />
<cfset variables.Address = arguments.Address />
</cffunction>
</component>
If you don't specify the full dot delimited path when these 2 components would be in separate directories, you'd get a an error - "The argument ADDRESS passed to function setAddress() is not of type Address."
So in the first case when the calling code and the CFC are the same directory, type seems to be just the name of the CFC without the suffix (.cfc). But in the second case, type is the dot delimited path from the webroot.
There's a third case we have to consider on the way, and that is if you add a mapping to a directory that has some CFCs in it. Let's say we log into the ColdFusion administrator and add a mapping to the components directory in the example above and call it /com. So for instance, if your webroot is at C:\CFusionMX7\wwwroot\, then C:\CFusionMX7\wwwroot\components\ is mapped to /com. Then you can specify the type using the mapping like so:
<component>
...
<cffunction name="setAddress" returntype="void">
<cfargument name="Address" type="com.Address" />
<cfset variables.Address = arguments.Address />
</cffunction>
<component>
To be clear, you could move a directory structure that contained CFCs anywhere on the server, create a mapping for it, and your dot path from the mapping to the component would work to specify the type.
So in the first case, type can be specified as the name of the component. In the second case, type has to be specified as the dot delimited path from the webroot to the component, and in the third case, type can be specified as the dot delimited path from an optional mapping to the component.
You with me so far? In essence, up until this point, type seems to mean, more or less, "a specific CFC in the file system of the server". CFAS will look in the same directory when checking the type if you specify only the component name, then it try the dot delimited path from the webroot to the component and see if that works, and if it doesn't, then it will look for any mappings that could resolve the type.
Now, we're still not done with our discussion of typing. But before we go on, we need to make sure we understand how inheritance works in ColdFusion.
Let's take a look at some example CFCs that demonstrate how inheritance works.
<cfcomponent displayname="Person">
<cffunction name="getName" returntype="string">
<cfreturn variables.name />
</cffunction>
<cffunction name="setName" returntype="void">
<cfargument name="name" type="string" />
<cfset variables.name = arguments.name />
</cffunction>
</cfcomponent>
The type and returntype above are specified as string, which is just a native ColdFusion variable type. Same as you'd find in the cfparam tag.
<cfcomponent displayname="Man" extends="org.Model.Person">
<cffunction name="fixTheCar">
...
</cffunction>
<cffunction name="playPoker">
...
</cffunction>
</cfcomponent>
<cfcomponent displayname="Woman" extends="org.Model.Person">
<cffunction name="goShoppingForShoes">
...
</cffunction>
<cffunction name="putOnMakeup">
...
</cffunction>
</cfcomponent>
In the example components above, you'll notice that there are differences between the Man and Woman components. But both the Man and Woman components extend the Person component. This is an example of inheritance. Here's how it's used. Let's first create an instance of a Woman, give her a name, and send her out shopping for shoes.
<cfset myWife.setName('Elisa') />
<cfset myWife.putOnMakeup() />
<cfset myWife.goShoppingForShoes() />
Because Woman extends Person, Woman can use methods contained in Person. So in the example above, the instance of Woman that we created, myWife, is able to use the setName() method.
In our example above, the Person object is referred to as the parent or supertype, and the Man and Woman objects are referred to at the child or subtype components. So when you employ inheritance with the extends attribute in the component tag and instantiate (create an instance of) your subtyped component, the methods of the supertype are merged into the instance.
It's important to note that it doesn't work the other way around. If you instantiate the supertype, which in our example would be Person, then the methods putOnMakeup() and goShoppingForShoes() would not be available within the instance. So for example, this is all that my wife could do if we instantiated her as a Person.
<cfset myWife.setName('Elisa') />
She wouldn't be able to put on her makeup or get yet another pair of shoes. Not sure if she'd like that too much ...
Anyway, now we're ready return to our discussion of type. Here's another object called FamilyCar to add to our growing collection of objects:
<cffunction name="getDriver" returntype="org.Model.Person">
<cfreturn variables.Driver />
</cffunction>
<cffunction name="setDriver" returntype="void">
<cfargument name="Driver" type="org.Model.Person" />
<cfset variables.Driver = arguments.Driver />
</cffunction>
</cfcomponent>
And now let's create an instance of the family car, and an instance of my wife, and send her out shopping again.
<cfset myWife = CreateObject('component','org.Model.Woman') />
<cfset myWife.setName('Elisa') />
<cfset myWife.putOnMakeup() />
<cfset ourCar.setDriver(myWife) />
<cfset myWife.goShoppingForShoes() />
I'm not sure if you noticed this, but let's take another look at the setDriver() function inside of FamilyCar.
<cfargument name="Driver" type="org.Model.Person" />
<cfset variables.Driver = arguments.Driver />
</cffunction>
Do you see how the argument Driver is expecting a type of org.Model.Person? Our Woman object has a type of org.Model.Woman, but she extends org.Model.Person. Hence, our Woman object also has a type org.Model.Person. In other words, Woman is inheriting the type "Person".
So to review where we are at now in our discussion of typing, type can be specified as the name of the component if the calling code is in the same directory. If not, type should be specified as the dot delimited path from the webroot to the component or the dot delimited path from a mapping to the component. And now we've just found out that type can also be specified as the supertype of the component (if of course the component has a supertype).
If i was going shopping with my wife, then we could create an instance of me, my wife and the family car, like so:
<cfset myWife = CreateObject('component','org.Model.Woman') />
<cfset myWife.setName('Elisa') />
<cfset me = CreateObject('component','org.Model.Man') />
<cfset me.setName('Nando') />
<cfset myWife.putOnMakeup() />
<cfset ourCar.setDriver(me) />
<cfset myWife.goShoppingForShoes() />
What's happening in our example above is a demonstration of polymorphism (subtype polymorphism, for anyone wanting to nail me to a tree). I'm a man, my wife is a woman, but since we are both subtypes of the class Person, we can both drive the car, because the setDriver() functions is expecting the type org.Model.Person. If it expected org.Model.Man for instance, my wife couldn't drive the car, and i (or some other instance of Man) would always have to take her shopping.
What's the big deal here? Why is this significant?
For a ColdFusion developer who is new to OO and is very used to dynamic typing, which in practice means you can pretty much ignore typing, the little polymorphic trick above that allows both me and my wife to drive the car is probably going to elicit a big SO WHAT? You might not get the point of all this. Because for you, ColdFusion has always done this kind of thing with it's native variable types. You can set a variable to a string, then to a number, then to an array and it just works. You've always gotten along fine without having to type any of your variables as strings or doubles or floating point numbers or whatever. Why should CFCs be any different?
If you were a new Java developer struggling to get the architecture of your model right so both you and your wife could drive the car and you finally got your app to compile, having just understood polymophism for the first time, you'd probably be jumping up and down with exitement. This would be a huge moment for you in your evolution as a Java developer.
But as a ColdFusion developer, your reaction is going to probably be a lot more muted ... more toward "So What?" or "I don't get it". You don't have to struggle with typing issues all the time. In fact, you don't have to struggle with typing issues at all.
You may already know this, but you can easily specify the type and returntype in our examples above as "any", or leave off typing entirely, and it'll work just fine.
<cfargument name="Driver" type="any" />
<cfset variables.Driver = arguments.Driver />
</cffunction>
... or ...
<cffunction name="setDriver">
<cfargument name="Driver" />
<cfset variables.Driver = arguments.Driver />
</cffunction>
Now in this example, both my wife and i can easily drive the car - and this loosely typed arrangement is what we ColdFusion developers are used to, especially the second example where the type declaration is left off entirely.
So in what way could explicitly typing our CFCs help us? Maybe we don't even need to understand "subtype polymorphism". Maybe we can get along just fine without it in ColdFusion.
The problem, on a theoretical basis at least, is that now this function will also allow our dog to drive the car. If we wanted our application to accurately model our life, then maybe typing isn't such a bad idea after all. In life, we don't let the dog drive the car. Polymorphism allows us to be both restrictive and flexible at the same time. We can restrict the setDriver() function to allow only the Person type, while still allowing flexibility so both me and my wife can drive the car, even if we are very different types of People.
The other problem with loosely typing our CFCs has to do with documentation. When a developer sees cfargument name="Driver" type="org.Model.Person", you know immediately that this function is expecting a Person object. If you see instead cfargument name="Driver" type="any", it's not clear if the function is expecting a string or something else. Here's a quote i'd like to throw in here:
"User-defined data types offer further benefit by providing valuable insight into program meaning. The declaration of types and their use in a program give a partial program specification. Like descriptive variable names, type declarations reveal clues to system structure."
Here's where we as ColdFusion developers need to understand for ourselves if typing is significant or not in our applications. In my example above, in real life, i actually would not at all be concerned that the dog would manage to drive the car. He'd never get the key in the ignition, much less manage to turn it. So our concern about only allowing persons to drive the car is theoretical. Should we introduce typing here and make things more complicated for ourselves just so we can make sure only Persons can drive the car? This is one question you need to ask yourself regarding typing, polymorphism, and the design patterns that are based on polymophism in the context of your own applications.
In other words, theoretically, we might think that we should explicitly type our returntype and type arguments in our CFC so only Persons can drive the car so we are good developers, but in practice, the dog will never manage to get even close to driving, and we're just a small family anyway, me and my wife. In my small app, in this sense i've got no practical reason to be concerned about strictly typing my setDriver() and getDriver() functions. IN MY OPINION ... ;-) I need to make this judgment call for myself.
But if one day i find that i am concerned in another much more complex app that's being worked on by a team of developers, and i need to be both restrictive in one way and flexible in another about which types can do something, then typing my CFCs and using (subtype) polymorphism might be a good idea. And if other developers are using the CFCs i write, typing helps to document what the the methods expect and what they'll return. I don't know when that day will come, but if it does, the app is probably quite complex and i might be providing a published API to it for other unknown developers to use ... in that scenario, yes, typing begins to have advantages. We can talk about it then.
Nevertheless, until i have a good reason to employ explicit typing in my CFCs along the lines of the above paragraphs, and until i'm skilled enough to do so in an intelligent way, i would rather not be pedantic about it. I'm suggesting we use type="any" until we really understand the benefits of explicitly typing our CFCs and the benefit is down to earth and practical. Until then, I can use the hint attribute to let other developers know what kind of object an argument might expect or a method return if i'm inclined to use typing because it helps document my methods.
There are also a few implications to take into account regarding the way ColdFusion does all it's type checking at runtime. One is that for non-native types in CFCs (like Person and Address in our examples above) it takes processing time to do so. If you're using a number of CFCs to process a request, that processing time could be fairly significant. And of course, instead of doing its type checking once like Java's compiler, ColdFusion checks the type over and over again with every request.
Just to be clear, native types (like array, boolean, date, numeric, string, etc) do not take any more processing time than using "any". (See http://blogs.sanmathi.org/ashwin/2006/08/27/quack/)
So i'm not in favor of explicit typing for explicit typing's sake. It makes no sense to me. In my view, that puts polymorphism and most of the design pattern stuff that goes with it in the back seat for me, and brings other basic fundamentals of OO design to the foreground.
A Java developer, on the other hand, has no choice about it. He/she has to explicitly type arguments and returntypes. OO literature is usually written by developers using strictly typed languages, so in that strictly typed context, polymorphism and design patterns become much more important.
In the beginning, if you are getting your feet wet with OO for the first time as a ColdFusion developer, i wouldn't worry too much about explicit typing, polymorphism, or design patterns. I know some people might disagree with me, maybe those who learned OO in a strictly typed language like Java. But these developers might not be aware of how confusing it is to try and understand concepts (for the first time) that are born and bred in a strictly typed environment while living in a loosely typed world.
What i would do instead is just concentrate on learning the mechanics of composition and inheritance as a first step. So that's what i'm going to concentrate on in my next posts on OO. I've never seen that well documented anywhere, and having a resource available like that would have helped me immensely.
I am of course a relative newbie at OO - it takes years to learn this stuff. So if you see anything that i've misunderstood or misrepresented here, please leave a comment.


regards
Regards
The problem with typing is that you have to jump through hoops if you don't have either multiple inheritance (Eiffel et al) or Interfaces (Java et al). Also, duck typing allows you to write much more dynamic reusable base classes.
If I had a large team of developers, I'd really recommend doing the model in Java or another language with all of the benefits of a compiler, static typing, interfaces, etc. It's like the old joke: Java is very good for large team development, which is good, as it takes large teams of people to develop anything useful in Java! It isn't that bad, but it is a trade off between safety and speed.
I'm really beginning to question the value of typing in ColdFusion on a variety of fronts. It adds a whole layer of complexity that is hard to understand the value of in the CF environment, especially for the CF programmer coming into OO for the first time.
I realize that if you already are fluent in OO, polymorphism and design patterns from another language, you'll certainly have an edge with OO in CF. But my feeling is that that knowledge is going to be hard to transfer to someone from within the CF environment. I have a sense it gets Lost in Translation, so to speak, as it moves from a strictly typed world into the loosely typed world of CF.
So i'd say, if you really know how to use typing and are sure of yourself in that world, use it if you want. If you don't ... you're going to have a much easier time absorbing OO in CF using the loose (duck) typing in your CFCs that you're familiar with, especially if you don't have an OO mentor at your side.
Also happy you mentioned the complexity angle: it's the simplicity of duck typing in CF that won me over given that the added complexity isn't as justified in CF as it clearly is in Java.
My self-taught journey into OO via Fusebox seems to have mirrored yours so I'm really pleased you're blogging.