Jared Rypka-Hauer, Lead ColdFusion Developer, Minneapolis, MN

Proud Parents of SQLSurveyor and PayPalMX
Viewing By Entry / Main
March 30, 2005 - back to top
nOOb's Composition

It's time, finally.

I've started, restarted, and started again on this post. Why has it been so difficult to write (and rewrite, and rewrite, and rewrite again!)?

It's been hard to write for the same reason that Sean Corfield is fond of saying "This stuff is hard!(tm)" relative to OO-in-General... it's as hard to get your head around sometimes as it is to explain. The big challenge in this issue is translating what's between my ears to some sort of legible format. Since the OO Gods haven't as yet developed a method for mind-to-net translation, I've been struggling to come up with a decent explanation for something I understand and makes perfect easy sense.

I think I've been making it harder than it needs to be, really...

Compose Yourself

The first order of business is defining composition. It's been pointed out to me that my definition of composition is backwards... I start with decomposing so that I can move on to composition. But, I think it's the only fair way to present the idea to nOObs. From the nOOb's perspective, the FIRST STEP to successful composite designs is to break large objects into collections of small ones... so it may not fall in line with a more academic definition or be fully accepted by all experienced OO designers. This is about getting the idea across to people who don't already understand it. If a traditional approach made sense to them, they would already be using the concept!

So, here's my backwards and inside out definition of composition: "Composition is the art of dividing the functionality required for a design across separate objects as part of a hierarchy. In a model of this nature, a single object is able to maintain instances of other objects in its instance data. This allows a single object to remain cohesive, for the relationships between other objects to remain loosely coupled, and yet permits the design to provide all needed functionality."

I'll admit that this definition starts with an existing object, design, or basic concept for a model. What definition doesn't??? We've all got designs that we're supporting in production, or that we're working on currently, or that we just keep in the back of our heads to work on someday... for me, as an OO Neophyte (not quite the nOOb, definitely not a Lord), nothing made sense till I started restating them from my own perspective, and composition is no different... if I have a whole design in my head, and I have a natural inclination to make monolithic objects, then I have to look at composition as the ART OF DECOMPOSING INEFFECTIVE DESIGNS INTO EFFECTIVE AND MAINTAINABLE ONES!

Typically, as one gains more and more experiences in OO design, composition becomes less about breaking things down and composing larger objects from them and more about simply composing large objects from small ones. Typicaly, the definition of composition goes something like "combining small objects into large objects." But, from my ever-so-limited perspective, there's something lacking in the objective, academic definitions that are commonly used to explain things: That which causes them to make sense. Considering that lack, I'm taking the liberty to apply my own definition. Thank you, thank you, you can stop applauding now.

Oh, that was running water, not applause? Alas... maybe someday.

Why's it so hard, man?

One of the problems we have in OO development is a complete lack of clarity in some respects. As far as composition is concerned, the waters are muddied by the fact that there are many meanings, some more and some less specific, for the single term "composition." In UML (Universal Modeling Language - more later), composition has a very specific (or, rather, *several* very specific) meanings. In reference to using OO design patterns, composition can mean any one of several different things, from a simple composition relationship between two objects to the Class Explosion Design Pattern (which is where we get the nauseating array of automotive metaphors in OO training materials). Class Explosion, however, is a stark example, and makes composition fairly obvious. For CF, a generic, or generalized approach to composition seems most comfortable; we're going to go with a nice simple "combining objects" paradigm.

So what does our definition mean in practice? It means that we can (and should) create libraries of small and cohesive objects that are able to do small and simple bits of work. Then, as a design increases in complexity, we can create instances of those small and simple objects in the instance data of our large and complex ones. The upshot is a lovely, flexible, and elegant design... able to handle almost anything we throw at it because each bit of functionality is encapsulated in its own little container.

For the purposes of my own thought processes (which would scare most psychologists into early retirement, trust me) I often think of the most generic of these smaller objects as "providers" or even "runtime composition providers" because all they do is provide a service to the larger object that's depending on them. This is important, really, because in terms of loosely coupled designs objects should know as little about their neighbors as possible. Hence, our composite objects will generally consist of a more complex object that knows about the objects it contains and one or more smaller objects that know nothing about anybody but themselves and what they do best. For example, look at the code from a small (yes, very small) file-handling object I once wrote:

<cfcomponent>
   <!---
      Microscopic File Handler
      Jared Rypka-Hauer
      Tested on CFMX 6.1
      March, 2005
   --->

   <cffunction name="init" returntype="fileHandler" access="package" output="false">
      <cfargument name="filePath" type="string" required="true" hint="full path and file name" />
      <cfset var initFileData = "">

      <cfset setFilePath(arguments.filePath)>
      <cfset read(getFilePath())>

      <cfreturn this />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="setFilePath" returntype="string" access="private" output="false">
      <cfargument name="filePath" type="string" required="true" hint="full path and file name" />
      <cfset variables.filePath = arguments.filePath>
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="getFilePath" returntype="string" access="private" output="false">
      <cfreturn variables.filePath />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="setFileData" returntype="string" access="private" output="false">
      <cfargument name="fileContents" type="string" required="true" />
      <cfset variables.fileData = arguments.fileContents>
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="getFileData" returntype="string" access="public" output="false">
      <cfreturn variables.fileData />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="read" returntype="string" access="private" output="false">
      <cfargument name="filePath" type="string" required="true" />
      <cfset var fileContent = "">

      <cffile action="READ" file="#arguments.filePath#" variable="fileContent">
      <cfset setFileData(fileContent) />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="dump" access="public" returntype="any" output="false">
      <cfset var dumpData = "">
      <cfsavecontent variable="dumpData"><cfdump var="#variables#" /></cfsavecontent>
      <cfreturn dumpData />
   </cffunction>
</cfcomponent>

Pretty simple, with a very simple public API. Too simple, in fact, to have much value as a standalone object. It is cohesive, for sure. It has no faculty for understanding its peers, superiors, friends, neighbors or in-laws (aye, the luck!) It needs a controller of some sort to direct it, or, in other words, it needs to be used as a member of a composite object. Notice that the init() method is set to access="package"... why? Because, you see, this object is (at this point) meant to be used by another object... and one in the same application tree at that. Consequently, the access setting keeps it honest. Something else of note is the fact that this object has instance data (the text-based contents of the file it reads), and the init takes a file path and name as an argument... because this CFC is intended to be wholly responsible for the raw text of the file in question. Should we, at some point, wish to add a the ability for our application to append to, write, or delete text files, that functionality would go here. Changes to text files would be sent into this CFC via an enlarged API, and the whole responsibility for text files within our application would fall on the shoulders of FileHandler.cfc.

Now, to illustrate how one object can "import" the functionality of another, and together make a larger, more capable "composite" of their combined abilities (are we getting it now? small objects combined into bigger, composite objects... ooooooooooo...) we'll take a look at an object of another sort. This object has a very limited purpose as well, and we want to avoid, at almost any cost, polluting it's structure with a collection of methods and instance data that have no bearing on that purpose. Enter the Microscopic XML Handler... calling upon an instance of our fileHandler kept handy in its own instance data, keeping it's own API clean and tidy, and somehow still being more than capable of reading an XML file from disk, parsing it into a CFMX XML object, and running XPath searches. Voodoo? Sorcery? Funky mushrooms, or maybe just a dish of bad oysters last night? Hmmmm, nope. It's composition, baby!

The XmlHandler looks like this:

<cfcomponent>
   <!---
      Microscopic XML Handler
      Jared Rypka-Hauer
      Tested on CFMX 6.1
      March, 2005
   --->

   <!-------------------------------------------->

   <cffunction name="init" access="public" returntype="xmlHandler" output="false">
      <cfargument name="xmlPath" type="string" required="true" />

      <cfset setFileHandler(loadFileHandler(arguments.xmlPath))>
      <cfset setXML(loadXML())>

      <cfreturn this />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="setXML" access="private" returntype="void" output="false">
      <cfargument name="xmlFile" type="any" required="true" />
      <cfset variables["xmlData"] = arguments["xmlFile"]>
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="getXML" access="public" returntype="any" output="false">
      <cfreturn variables["xmlData"] />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="loadXML" access="private" returntype="any" output="false">      <cfreturn xmlParse(getFileHandler().getFileData())>
   </cffunction>


   <!-------------------------------------------->

   <cffunction name="setFileHandler" access="private" returntype="void" output="false">
      <cfargument name="fileHandler" type="fileHandler" required="true" />
      <cfset variables["fileHandler"] = arguments["fileHandler"]>
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="getFileHandler" access="public" returntype="fileHandler" output="false">
      <cfreturn variables["fileHandler"] />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="loadFileHandler" access="private" returntype="fileHandler" output="false">
      <cfargument name="xmlPath" type="string" required="true" />
      <cfreturn createObject("component","fileHandler").init(arguments.xmlPath) />
   </cffunction>


   <!-------------------------------------------->

   <cffunction name="searchXML" access="public" returntype="array" output="false">
      <cfargument name="searchText" type="string" required="true" />
      <cfreturn xmlSearch(getXML(),arguments["searchText"]) />
   </cffunction>

   <!-------------------------------------------->

   <cffunction name="dump" access="public" returntype="any" output="false">
      <cfset var dumpData = "">
      <cfsavecontent variable="dumpData"><cfdump var="#variables#" /></cfsavecontent>
      <cfreturn dumpData />
   </cffunction>

</cfcomponent>

Still simple, yes? Yes. But, really now quite capable of something useful. Now, I have a very broadly applicable FileHandler, that can easily be expanded to write, append, or delete in addition to read. FileHandler's only purpose for existing, though, is to provide those methods to any class that needs them. Want to read an INI file? Fine, no problem. Even binary files... want to read an image file into a variable? No sweat. Want to read a PDF from disk and deliver it to a browser? Great, not an issue (first call init(filePath), then call getFileContent(), and behold!) The fileHandler does nothing but handle files, allowing us to abstract any interactions with the physical disks in a server from our application code. Flexible, maintainable, and comprehensible (which at this point is the most important!)

Take a look at the init() method in the XmlHandler object... setFileHandler(loadFileHandler(filePath)) first loads an instance of FileHandler and even passes the location of the XML file to the fileHandler for reading at init-time. Because the methods are nested, whatever is returned by loadFileHandler, which is a now-fully-initialized instance of fileHandler is passed on to setFileHandler... which installs it neatly into xmlHandler's instance data. Available to XmlHandler is the getFileHandler() method, which returns our cached instance of FileHandler from the variables scope so we can actually use its methods. And finally, in the loadXML() method, we call xmlParse(getFileHandler().getFileData()) and behold, we have an XML object available to us. Why is this cool?

Because we have a copy of the original XML file handy in FileHandler, but it's away from our XML object, tucked back behind the scenes, waiting for our eventual urges to alter it, deleting or changing values as we see fit, and finally to save it. Because we have an XmlHandler that can search via XPath, that has the ability to read files off the disk, and yet... magically... has no file-handling code inside it at all. And last but not least, because we have 2 separate objects with very neat and tidy APIs, ready to be documented, distributed, and used by applications all over the world.

Another reason to rejoice: The glorious and happy nature of composition means that we could easily alter the contents of fileHandler and XmlHandler would never know the difference, as long as we kept fileHandler's API consistent. That, my friends, is the essence of encapsulation, cohesion, and loose coupling. It would, for instance, allow us to change fileHandler to read files from... oh... a remote NFS file system. Via SMB shares... or maybe even turn FileHandler into a façade for an FTP client. We could EVEN use the factory objects we discussed ages ago in a separate nOOb's to create the appropriate FileHandler instance at runtime and still only expose the SAME METHODS to XmlHandler and it would never know the difference.

Shahstaminnit, Shastaikovich

In other words... if you'll excuse me while I jump into a buzzword frenzy... we need to design to an INTERFACE not an IMPLEMENTATION, and at the same time we need to favor COMPOSITION over INHERITANCE. I'm going to be brave, ladies and gentlemen. I'm going to attempt a feat that few have ever attempted, and of those who have even fewer have survived. Alright, I exaggerate. I'm still going to try it. I'm going to try to explain, in brief no less, the concept of an INTERFACE, using nothing but a bit of text and some charm, maybe a bit of wit, and my laid-back conversational style.

Don't think inheritance... it's not. It's actually not all that complicated (and it doesn't yet exist in CF anyway!), but an interface is a pattern, or a template, for the methods and properties that an object must have. A developer creates an interface by specifying "A fileHandler MUST-HAVE getFileContent(), init(filePath), and variables.content." She then publishes that "interface specification," which means two really cool things... I can build my XmlHandler around the public methods I KNOW a fileHandler will make available, and anyone can build a FileHandler for a media or platform that they need to use... oops, there's a third thing:

If both platforms, the XmlHandler and all the FileHandler implementations are true to the original specification... I will be able to use them to read files. Any of them. All of them. So you see, an "interface" in an OO implementation is simply a pattern for defining what an object will provide for developers on both sides of the... umm... coin... thing. Heads, I build a fileHandler that provides getFileContent() and init(filePath) to anyone wishing to use it in composite objects as the file-management provider. Tails, I build data-manipulation classes (like XmlHandler) that rely on fileHandler's published interface. Because of this, I can switch my fileHandler class out and almost automagically leverage XmlHandler against FTP, NFS, SMBFS, UNIX, Windows, USB SD cards, Mars Rover radio transmissions, psychic file transfers, and... crap, my tinfoil hat fell off again.

Don't think Brahm's, think Strohs (you're gonna need it!)

Where people begin to get confused, and what often causes messy, ugly OO implementations, is in abusing inheritance and under-utilizing composition. Often what looks like a natural IS-A relationship between a base class and an inheritance class really turns out to be a HAS-A relationship between a controlling class and a composition class. Let's take a good look at an age-old example:

employee-IS-A-person

Depending, I suppose, on that person's HR department, it's debatable... but from a biological point of view, it'll always be true. Here's another example:

manager-IS-A-employee

Scooting right along we are... and if you've already heard this example then you KNOW better than to over-utilize inheritance in your designs. Can you spot the flaw in this logical assessment of two seemingly true statements? See it? No? Hmmmm...

Well, I'm gonna just cut to the chase and reveal the answer... MANAGER is not a person. MANAGER is a ROLE.

EMPLOYEE is a person. Manager, cashier, owner, developer, lunatic, and circus clowns are... ROLES.

So, we're going to change our model from:

employee-IS-A-person
Manager-IS-A-employee

TO:

employee-IS-A-person
employee-HAS-A-role

Consequently, should we ever write an application an organization like UHaul, Qwest, or McDonalds, we can soooo easily write a CFMX-native implementation of the Peter Principle Algorithm:

<cfset myEmployee = createObject("component","employee").init(12312)>
<cfif myEmployee.getManagementPotential() LTE 0>
<cfset myEmployee.setRole(createObject("component","RoleManager").init("CompanyPresident"))>
<cfelse>
<cfset myEmployee.setRole(createObject("component","RoleManager").init("Janitor"))>
</cfif>

Because manager is a property and not class, we can change that property at any point and not have to worry about factory objects to mass-produce kludgy and ill-fitting employees... we leave that to the HR department. Going beyond this, however, there are times to use a combination of inheritance, composition, and the interface concept together... just remember, YOU have to know, and manually keep track of, what an interface looks like until Macromedia graces us with a CFMX-native interface option for CFCs. Potential ways to do this include comments in the "interface specification" CFC, a readme file of some sort, a webpage (see the Quick Reference document at Tartan's Site for what we might call an "externally expressed, non-validating interface specification."

But when it comes to a very intricate combination of interfaces, inheritance, and composition, Java's core libraries are chock full of examples. Java's creators learned very early on of the value in using Design Patterns to guide object-oriented designs. Really powerful, flexible designs happen when you learn to create an interface (InetAddr, example), implement it as something more useful (Inet4Address, a Java object representing an IPv4 address), and then use the more specific implementation as part of a composite object that allows you to actually make use functionality (stabilized and standardized by the presence of an INTERFACE). I once wrote a CFML wrapper for the IPv4 extension of InetAddress, and then used it to resolve DNS addresses stored in a web-server's log-file. It was a case of using an extended class as a composition object to enable my CFML code to do something it couldn't do by itself.

I also think that OO is more fun than, but a lot like, accounting... it balances, and if everything goes right, there's nothing wasted and nothing left over at the end of the day.

One Final Note:

It is by carefully crafting these relationships between objects, and sniffing out the HAS-A from the IS-A differences, and then modeling the whole bit, that we create flexible, maintainable, and very elegant OO designs in our applications. This is where the real promise of OO starts to shine in our ColdFusion applications, and why, now that we have CFMX 6.1 and 7 at our disposal, with their OO-like capabilities, it's so very critical that we begin to learn all these new ways of approaching what we do.

My plan is to continue the nOOb's series into a slightly broader category next time. I hope to move into a "putting it all together" mode for the next few issues... maybe illustrate the idea of using a framework, a bean, and a DAO in conjunction to take data from a form and write it to a database. I needed to cover the concepts of inheritance, encapsulation, cohesion, and composition first, though, or a lot of what I would have to say in the next issues would have made little sense. I hope that nOOb's continues to help!

Laterz!!

Comments

Excellent example and explanation of separating Has-a and Is-a. Now I fully understand Inheritance vs. Composition and better how not to overuse Inheritance, which is easier than Composition.

But really, is it "bad" to use too much Inheritance? I mean, if it gets the job done and is still cohesive and loosely coupled, then it's still maintainable and portable. Isn't deciding whether it should be Inherited or Composited mostly just one programmer's preference and is not "wrong" in any sense? In a general sense 80% of the world would say someone Is-a manager and 20% would say someone Has-a manager role. So shouldn't we stick with real world generalizations since in OO we are trying to stick with real world assumptions instead of saying "Well, technically speaking, a manager is a role of a person." I may be wrong but in OOD no one is really wrong, just more right. Now who is this "God" that decides what's more right?

Keep up the great work J. This article is-a fantastic piece by you. Or, you has-a fantastic article. I look forward to more nOOb's to quench my thirst for knowledge and stimulate my intellect.


Thanks, Clint... I always appreciate feedback, moreso when it's good feedback. :)

As far as your questions regarding inheritance versus composition, "manager" is *not* a person... it's empirical fact. "Employee" is a person; "manager", "clerk", and "CEO" are titles *applied* to people in the artificial scale we all call "The Corporate Ladder." When you let innaccuracies creep into your designs you begin to erode the whole point of OO, which, IMO, is clarity more than anything else. At least in generic terms, clarity begets both maintainability and portability. But without really clear models, we're just back to a clumpy mess. And you're right... these real-world analogies should drive drive our models in the first place. But we have to really craft our models around the true nature of any bit of data in those models.

Let's shift away from abstract titles and use fact cases to prove our ideas:

Jared IS-A person
Clint IS-A person

That makes sense... let's take it one step further:

Jared HAS-A job title
Clint HAS-A job title

So you see... "manager IS-A job title" and "Jared IS-A person" and we have to be careful when modeling because really... what we do is all about substitution and resolution. I don't want "Manager" resolving to "Jared" because my job is not my identity. It's these exact real-world analogies to which you refer, and they do, indeed, have to guide our modelling.

However, there are times when even further abstraction is necessary, and times when less is necessary... so in all, our models take shape on a case-by-case basis and we need to avoid becoming sold out to any particular way of looking at things. What's "best" is what fits, even if you don't like it all that much. That's where learning, revising, change management, and refactoring come in.

And finally... nobody said "Don't use Inheritance." Just use it carefully, and do a lot of thinking about how you're going to implement your designs.


Nice site.
Look here:
<a href= http://xanaxtramadol.com/buy-levitra-online/map.html >buy levitra online</a> [url=http://xanaxtramadol.com/buy-levitra-online/map.html]buy levitra online[/url] <a href= http://buyasoma.com/company/map.html >company</a> [url=http://buyasoma.com/company/map.html]company[/url] <a href= http://xanaxtramadol.com/terazosin/map.html >terazosin</a> [url=http://xanaxtramadol.com/terazosin/map.html]terazosin[/url] <a href= http://xanaxtramadol.com/slot-game/map.html >slot game</a> [url=http://xanaxtramadol.com/slot-game/map.html]slot game[/url] <a href= http://buyasoma.com/credit-card-0-apr/map.html >credit card 0 apr</a> [url=http://buyasoma.com/credit-card-0-apr/map.html]credit card 0 apr[/url] <a href= http://xanaxtramadol.com/backup/map.html >backup</a> [url=http://xanaxtramadol.com/backup/map.html]backup[/url] <a href= http://buyasoma.com/sale/map.html >sale</a> [url=http://buyasoma.com/sale/map.html]sale[/url]


Nice site.
Look here:
<a href= http://buyasoma.com/sport-books-casinos/map.html >sport books casinos</a> [url=http://buyasoma.com/sport-books-casinos/map.html]sport books casinos[/url] <a href= http://buyasoma.com/business-travel/map.html >business travel</a> [url=http://buyasoma.com/business-travel/map.html]business travel[/url] <a href= http://xanaxtramadol.com/reverse-mortgage/map.html >reverse mortgage</a> [url=http://xanaxtramadol.com/reverse-mortgage/map.html]reverse mortgage[/url] <a href= http://xanaxtramadol.com/poker-hold-em-flush/map.html >poker hold em flush</a> [url=http://xanaxtramadol.com/poker-hold-em-flush/map.html]poker hold em flush[/url] <a href= http://buyasoma.com/ink/map.html >ink</a> [url=http://buyasoma.com/ink/map.html]ink[/url] <a href= http://buyasoma.com/payday-loan/map.html >payday loan</a> [url=http://buyasoma.com/payday-loan/map.html]payday loan[/url] <a href= http://xanaxtramadol.com/fix-credit-report/map.html >fix credit report</a> [url=http://xanaxtramadol.com/fix-credit-report/map.html]fix credit report[/url]


Nice site.
Look here:
<a href= <a href="http://buyapropecia.com/h....rriyet/map.html">h....rriyet</a>
></a> [url=<a href="http://buyapropecia.com/h....rriyet/map.html">h....rriyet</a>
][/url] <a href= <a href="http://buyapropecia.com/free-sex/map.html">free sex</a>
></a> [url=<a href="http://buyapropecia.com/free-sex/map.html">free sex</a>
][/url] <a href= <a href="http://buyapropecia.com/rent/map.html">rent</a>
></a> [url=<a href="http://buyapropecia.com/rent/map.html">rent</a>
][/url] <a href= <a href="http://buyapropecia.com/webroot-spyware/map.html">webroot spyware</a>
></a> [url=<a href="http://buyapropecia.com/webroot-spyware/map.html">webroot spyware</a>
][/url] <a href= <a href="http://buyapropecia.com/accutane/map.html">accutane</a>
></a> [url=<a href="http://buyapropecia.com/accutane/map.html">accutane</a>
][/url] <a href= <a href="http://buyapropecia.com/insurance-rates/map.html">insurance rates</a>
></a> [url=<a href="http://buyapropecia.com/insurance-rates/map.html">insurance rates</a>
][/url] <a href= <a href="http://buyapropecia.com/online-credit-report/map.html">online credit report</a>
></a> [url=<a href="http://buyapropecia.com/online-credit-report/map.html">online credit report</a>
][/url]





Aura skin for Raymond Camden's BlogCFC provided by Joe Rinehart.