I attended Hal Helms' talk on OO modeling at cf.objective(). As part of the talk, Hal reiterated the tried and true design principle to "favor composition over inheritance". During the Q&A session, many people seemed to be confused about inheritance vs. composition. The questions weren't so much regarding the definitions, which are fairly straightforward (inheritance is an "is-a" relationship while composition is a "has-a" relationship). The issue seems to be when to use one over the other. Additionally, if you're really supposed to prefer composition, when do you use inheritance at all? Due to the breadth of the topic of OO design, we ran out of time before this specific question could be explored fully. I thought I'd post to praise Hal for his excellent talk as well as add my thoughts to the discussion.

I'll step through the same example Hal used. Consider an Employee class with two subclasses, SalariedEmployee and HourlyEmployee:

Potentially bad use of inheritance

On the surface this seems to make perfect sense. But if you think about it for a moment, it starts to fall apart. Most importantly, what happens if one of your hourly employees becomes a salaried employee? There is no way to make this change at runtime without destroying the original HourlyEmployee object (Hal had a great picture of a poor Employee object begging for mercy). Thus, any other objects with handles on the old object are also in trouble. A better solution is to use composition:

Favoring composition over inheritance

Instead of Employee knowing how to determine its pay, that is wrapped up in another object. It now becomes possible to change the pay type of the Employee without destroying it. You can simply call a setter in your Employee object to swap out the kind of pay being used. This is where we get to the root of the question of where does inheritance come into play.

I think that when inheritance is mentioned, the first impulse is often to look at it as a way to reduce code duplication. You can push code that is used by more than one subclass up into the superclass so you don't have duplicate methods in the subclasses. We all loathe duplication, so this is indeed a nice advantage of leveraging inheritance.

However, I don't think this is the most useful way to look at inheritance. Far more important is using inheritance to encapsulate variation:

Using inheritance to encapsulate variation

We knew there would be multiple kinds of pay types, so to handle this Hal demonstrated using the Strategy pattern. It's not necessary that you know all about this pattern, what is more important is to look at what inheritance is giving us in this situation. Because our concrete pay classes are extending an abstract PayStrategy class, we can completely hide the fact that there are multiple pay types from the rest of the system. Because other classes (like Employee) are only aware of the abstract PayStrategy class, we are free to add or remove concrete subclasses at will. We could have two kinds of pay classes or we could have ten. The rest of the system will never know.

For example, if I have a handle on an employee object Joe and I want it to determine its pay, I call Joe.calculatePay(). Internally, Joe is using composition to hold an HourlyPayStrategy object, and he calls calculatePay() on it to ask it to calculate the pay value. But the crucial thing to understand is that Joe has no idea which kind of PayStrategy he is holding on to. He just knows he has some kind of PayStrategy. Because all of the concrete subtypes of PayStrategy can respond to the calculatePay() message, you can call calculatePay() on any of them without knowing what type they actually are!

In OO design this is expressed by the principle "design to interfaces, not to implementation". Using inheritance as part of the Strategy pattern lets us hide our varying pay types from the rest of the application*. In my opinion, this is the place where the use of inheritance is most beneficial. Hopefully this helps to clear up some of the confusion about when to use inheritance. If you have any thoughts on the matter feel free to comment.

* Quick side note to say this isn't completely true. One object will need to know about the variation, and that is the factory that creates an Employee and determines which of the concrete PayStrategy objects to create and give to it. However, the benefit is still exactly the same. Knowledge about the variation is encapsulated in exactly one place: the factory.

Updated 12:40 pm: Some example code follows:

<!--- Index.cfm File will simulate any external code that needs to get and use an Employee --->
<cfset employeeFactory = CreateObject('component','EmployeeFactory').init() />

<cfset joe = employeeFactory.getEmployee('Joe') />
Joe's pay is hourly: <cfoutput>#joe.calculatePay()#</cfoutput>

<br />
<br />

<cfset tim = employeeFactory.getEmployee('tim') />
Tim's pay is salary: <cfoutput>#tim.calculatePay()#</cfoutput>
		

<cfcomponent name="EmployeeFactory" hint="I handle the steps to create an Employee instance.">
	
	<cffunction name="init" access="public" returntype="EmployeeFactory" hint="Constructor.">
		<cfreturn this />
	</cffunction>
	
	<cffunction name="getEmployee" access="public" returntype="Employee" output="false" hint="Create an employee instance.">
		<cfargument name="employeeName" type="string" required="true" hint="In a real app this could be an ID or a query, and the factory would create the necessary CFCs dynamically rather than a hardcoded switch." />
		<cfset var local = structNew() />
		<cfswitch expression="#arguments.employeeName#">
			<cfcase value="Joe">
				<cfset local.pay = CreateObject('component','HourlyPayStrategy').init() />
				<cfset local.employee = CreateObject('component','Employee').init(local.pay) />
			</cfcase>
			<cfcase value="Tim">
				<cfset local.pay = CreateObject('component','SalariedPayStrategy').init() />
				<cfset local.employee = CreateObject('component','Employee').init(local.pay) />
			</cfcase>
		</cfswitch>
		<cfreturn local.employee />
	</cffunction>

</cfcomponent>
		

<cfcomponent name="PayStrategy" hint="Abstract pay type.">
	
	<cffunction name="init" access="public" returntype="PayStrategy" hint="Constructor.">
		<cfreturn this />
	</cffunction>
	
	<cffunction name="calculatePay" access="public" returntype="string" output="false" hint="Abstract method.">
		<cfthrow type="PayStrategy.AbstractMethod" message="The abstract method calculatePay() must be implemented by a subclass." />
	</cffunction>

</cfcomponent>
		

<cfcomponent name="HourlyPayStrategy" extends="PayStrategy" hint="I make calculations for hourly pay.">
	
	<cffunction name="init" access="public" returntype="HourlyPayStrategy" hint="Constructor.">
		<cfreturn this />
	</cffunction>
	
	<cffunction name="calculatePay" access="public" returntype="string" output="false" hint="Calculate the pay.">
		<cfreturn "Hourly pay is number of hours worked times hourly rate." />
	</cffunction>

</cfcomponent>
		

<cfcomponent name="SalariedPayStrategy" extends="PayStrategy" hint="I make calculations for salaried pay.">
	
	<cffunction name="init" access="public" returntype="SalariedPayStrategy" hint="Constructor.">
		<cfreturn this />
	</cffunction>
	
	<cffunction name="calculatePay" access="public" returntype="string" output="false" hint="Calculate the pay.">
		<cfreturn "Salaried pay is annual salary divided by number of pay periods." />
	</cffunction>

</cfcomponent>
		

<cfcomponent name="Employee" hint="I am an employee.">
	
	<cffunction name="init" access="public" returntype="Employee" hint="Constructor.">
		<cfargument name="pay" type="PayStrategy" required="true" />
		<cfset setPay(arguments.pay) />
		<cfreturn this />
	</cffunction>
	
	<cffunction name="getName" access="private" returntype="string" output="false"  hint="I return the name.">
		<cfreturn variables.instance.name />
	</cffunction>
		
	<cffunction name="setName" access="private" returntype="void" output="false"  hint="I set the name.">
		<cfargument name="name" type="string" required="true" hint="name" />
		<cfset variables.instance.name = arguments.name />
	</cffunction>
	
	<cffunction name="getPay" access="private" returntype="PayStrategy" output="false"  hint="I return the pay.">
		<cfreturn variables.instance.pay />
	</cffunction>
		
	<cffunction name="setPay" access="private" returntype="void" output="false"  hint="I set the pay.">
		<cfargument name="pay" type="PayStrategy" required="true" hint="pay" />
		<cfset variables.instance.pay = arguments.pay />
	</cffunction>

	<cffunction name="calculatePay" access="public" returntype="string" output="false" hint="Calculate the pay for this employee. Note that the Employee has no idea which kind of PayStrategy it is calling calculatePay() on!">
		<cfreturn getPay().calculatePay() />
	</cffunction>

</cfcomponent>
		

Comments Comments (19) | del.ico.us del.icio.us | Digg It! Digg It! | Linking Blogs Linking Blogs | 10257 Views

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)

  • # Posted By Mike Rankin | 5/9/07 11:27 AM

    I was with you right up until the end there. It would be nice to see your last diagram implemented in cf, although I know that would be a lot of work and I don't expect you to do it.

    The part that I found confusing was how does Joe wind up with an HourlyPayStrategy stored internally and not know what kind of PayStrategy is stored? I guess putting the particular type of PayStrategy is a job for the DAO?

  • # Posted By Brian | 5/9/07 11:32 AM

    Joe doesn't know what kind of PayStrategy he has because a factory provides it to him (see my final note in the post). So the knowledge of which type of pay to inject into an Employee is kept in only one place: in the factory. Make sense? Maybe if I have time I can whip up the code and add it.

  • # Posted By Brian | 5/9/07 12:02 PM

    I've updated the entry with some sample code. Hopefully this makes things more clear and not more confusing since the real point was the IDEA of when inheritance is most useful.

  • # Posted By Patrick McElhaney | 5/9/07 12:27 PM

    Joe has a pointer to a specific type of PayStrategy, but he doesn't know which type it is. He doesn't even know what the various types are. He just knows that variables.intstance.pay points to some subclass of PayStategy. Or, if you're using duck typing, he just knows that variables.instance.pay has a calculatePay() method.

  • # Posted By Brian | 5/9/07 12:44 PM

    Exactly Patrick, well put.

  • # Posted By Kurt Bonnet | 5/9/07 2:12 PM

    I think this post was very good in general but didn't clearly make its point (or what I thought was the point) at the end. I like how you clearly showed the use of composition, but the reason why you chose to use an ABSTRACT CLASS for Pay Strategy instead of an INTERFACE was not clear. Abstract classes and interfaces both provide a similar level of abstraction to allow for implementation variations, but an interface allows for an even GREATER level of variation than an abstract class does. So I'm assuming you chose an abstract class because you have reached a point where you feel the scope is narrow enough to be able to use inheritance without getting bitten by its limitations. My guess is that this is the overall intention of your post. Great post by the way, I'm not trying to criticize at all. Loved your article on builder about the State pattern, I look forward to more of your writings.

  • # Posted By Kurt Bonnet | 5/9/07 2:17 PM

    Duh, I'm in a Java mindset right now. CF doesn't support interfaces right now, so pseudo-abstract classes are the closest things. Although CF8 WILL give us interfaces, so I think knowing WHEN to choose an abstract class over an interface is important to discuss.

  • # Posted By Brian | 5/9/07 2:37 PM

    Totally true Kurt. The choice of using an interface vs. an abstract class is another can of worms altogether!

  • # Posted By Jaime Metcher | 5/9/07 10:03 PM

    Great post.

    Just want to add that the flip side of encapsulating variation is encapsulating standards using a concrete superclass and the "template method" pattern.

    I've historically had a hell of a time getting junior developers to consistently cover all the bases when developing procedural code. If (and it's a big if) I can codify a best practice into a template method, then all I have to tell a developer is "Extend this class, implement that method" and I know that the bases are covered by the superclass. Kind of a micro inversion of control technique.

    Example: form processing. Our general process is:

    a. did a form get submitted?
    b. was it my form? (maybe multiple forms on a page)
    c. did the whole form get transmitted? (occasionally we see partial form posts)
    if all of the above are true,
    d. process the form.

    I'm still discovering forms created two years ago where some developer forgot all or part of the standard process (obviously code reviews weren't happening). These days all they need to do is subclass the generic form class and implement part "d". Code reviews (when they happen) are easier - one thing to check, is this a subclass of the form object?

  • # Posted By John Farrar | 5/10/07 8:12 AM

    We need a good UML tool for modeling CFCs. :)

  • # Posted By Ilya Fedotov | 5/11/07 9:56 AM

    Good post. Thanks for code examples. Right now I am leaning towards not using abstract class or interface in CF, like you did with PayStrategy.
    It helps to convert some of the Java thinking to CF but it seems pretty useless. You would need to duck type pay in employee though.

  • # Posted By Fred | 5/14/07 11:14 AM

    Thanks for writing this, very interesting. I've been reading head first design patterns, and it's nice to see a real world CF example of Strategy. Out of interesting, during the employee instantiation, would the DAO read operation occur within the Employee factory? Or, would you encapsulate the DAO elsewhere?

  • # Posted By Paul Marcotte | 5/15/07 2:11 AM

    Brian,

    Great example of both composition, inheritance and Strategy pattern. Nice work. I don't know how many time I tried reading through the Recognition Strategy example in Martin Fowler's PEAA book before giving up. A light is turning on now and I hope it stays lit...

    One observation. For the EmployeeFactory, I can understand how passing in a query would provide the factory with the infomation needed to decide which PayStrategy to create (a PayType attribute perhaps). Passing in an ID, on the other hand, would require the factory to either query the database directly, or delegate to a DAO. If we know in advance what the PayType is for an employee, might it be better to simaply pass the type to the factory method as EmployeeFactory.getEmployee('Hourly'), or EmployeeFactory.getEmployee('Salary')? I get that the example code is simplified, I'm just imagining how I might implement this in a real app.

  • # Posted By Brian | 5/15/07 8:45 AM

    Hey Paul. I don't think just passing the type is enough. Because there will probably be a bunch of other things that need to be set or injected into the Employee that is being created. Especially if it is an existing employee. So I'd probably go with passing a query in, or with having the Factory hold a reference to the EmployeeDAO so it could execute the query and load the proper data itself. Make sense?

  • # Posted By Paul Marcotte | 5/15/07 4:32 PM

    Hi Brian,

    Yup. I guess I was thinking about the EmployeeFactory being part of an EmployeeService. I recently received an education on cohesion and separation of concerns from some smart people and thought that a factory typically does not make use of a DAO. Although, in the absence of a Service class, that makes sense to me!

  • # Posted By Aaron Roberson | 5/25/07 9:07 PM

    Brian, make this comment box bigger!

    Basically, what you are saying is that we should wrap the logic up in a seperate CFC when it is reusable AND varies per implamentation, then inject it into our objects, right?

  • # Posted By Sinuy | 6/4/07 1:57 AM

    hi Brian
    i'm new to OOP. where's the 'variables.instance.name' in Employee.cfc from/goes to?

  • # Posted By Brian | 6/4/07 10:03 AM

    @Aaron: yes!

    @Sinuy: the name instance variable isn't used in any of the example code, it was just there to give Employee some more realistic data and behavior.

  • # Posted By sacundim | 5/1/08 8:32 PM

    But note that in your final diagram, the one with Employe, PayStrategy, HourlyPayStrategy and SalariedPayStrategy, there is no actual implementation inheritance. All that PayStrategy is doing is specifying the common interface to two completely separate implementations. In Java terms, PayStrategy is an interface, not a class.

    In other words, your "inheritance" solution is, in fact, a pure composition solution. The classic "use composition, not inheritance" argument is an argument against implementation inheritance.

    I argue that using the term "interface inheritance" for a mechanism that just combines abstract data type definition and dynamic type-based method dispatch is just misleading. While true inheritance (i.e., "implementation inheritance") requires ADTs and dynamic dispatch, the converse is not true. And a language with just composition + ADTs + dynamic dispatch is all you need for your example here.