Composition vs. Inheritance

A little while ago, someone posted to our website a question of design. I was in the middle of crafting a response when I decided that I probably couldn’t speak for the whole of Atomic Object, and since this response would represent Atomic, I asked for input from all corners. I think the answers are pretty interesting. So I posted the question, followed by a long series of responses… Original question from Mike:

“I was wondering if I may ask your opinion on a design issue. I read your tutorial pages on Composition (Aggregation) vs. Inheritance and found them very instructive toward my OO design.

Application: Let’s say you have an “Asset” class whose objects will be part of a collection. The purpose is to keep track of the hardware assets(computers) assigned to an employee. An Asset has an ID#, Manufacturer, Warranty Expiration Date etc. It also has a Type. Type could be a PC, laptop, or server (Whichever is assigned to a given employee).

A laptop has a battery but neither a server nor a PC do. A laptop has a docking station which neither PCs nor servers do. A server can have multiple processors and/or hard drives to keep track of which laptops don’t.

No functionality is required for derived classes they just have attributes both common to Asset yet specialized to themselves, as just described.

Would it be more appropriate to use inheritance (a laptop class, PC class, and server class all inherit from an Asset class [sharing common Asset attributes such as AssetID, AssetManufacturer, etc.]) or just have an Asset class where non applicable attributes are just assigned NULL values?

Just curious your thoughts on that. Thanks!” Dave said:

The best way to know what to do with your Asset class is to ask yourself quastions like:
  • What common interface do the Assets actually have? What process needs to refer to them generically, and in what fashion? How will Assets be used by other code?
  • Do your different assets really have some common fields, and some specialized fields?
  • Eg, it might make sense for an Asset to have a list of Components, and Components could describe harddrives, batteries, or whatever. After all, computers can potentially vary widely in their composition.
I personally treat the inheritance mechanism as a tool for reducing/removing code duplication AFTER the duplication has occurred in straight-forward code… NOT as a design tool. When I ask myself questions about what requirements I need to fill and what objects I’ll need, the answer does not come in the form of an inheritance diagram; it is a picture of system components that use each other to get the job done. Sometimes duplicate code can be extracted into a common base class… however, if you’ve identified a piece of functionality that is needed by two different objects, why not create a third object to do that work, and provide that object to the two needy objects? It’s easier to do, easier to test, and easier to understand on sight for someone who didn’t design the code. Zack said:

I was going to suggest the same thing. Use the Strategy Pattern.

From the example in Head First Design Patterns:

Different ducks need different ways of quacking. Examples they gave were:
  1. MallardDuck
  2. RedheadDuck
  3. RubberDuck
  4. DecoyDuck
Instead of creating a duck for each type, write a duck that is instantiated with individual “Quack” Behaviors.
  1. Quack
  2. Squeak
  3. MuteQuack
The best part of this, he will most likely find commonalities to each behavior and code will be abstracted there rather than at each individual Asset. Greg said:

Having had to debug and maintain a significant amount of C++ code that I have developed, I’ve found that inheritance is a tricky mechanism and is usually a bit confusing to someone who didn’t write the code, unless it is done VERY cleanly. I would say that adding helper objects to eliminate code duplication should always be leaned towards rather than inheritace, because it is a concrete object that you are dealing wih instead of just a chunk of included code (via inheritance). And yeah, a real object is definltey easier to test than a base class.

Carl offered a slight alternative:

An Asset could have a (possibly null) list of interface types (Attributes, Accessories, Parts, etc). Concrete classes that satisfy this interface can be created to represent the things like hardrives, processors, etc. Likely they’d be very lightweight, data-only classes.

Scott added:

Following Carl’s idea, I was thinking that an Asset could have a collection of other IAsset(s). My thinking is that removable hard drives, power supplies, batteries, etc are themselves assets to be tracked in inventory and it seems possible that an asset in another asset’s collection might have its own associated assets.

Patrick followed with:

I think Scott/Carl have refined the original idea quite well. An Asset contains a collection of other Assets. There is no need for any inheritance, or even delegation. It is just an implementation of the Composite design pattern.

He explicitly stated that the objects have no behavior. For that reason I can’t see why you would want to have different classes represent different types of components. Everything is an Asset. Some Assets are made up of other Assets. You don’t do anything with an Asset, other perhaps extract information from it about itself and its child asset (Visitor pattern?).

Filed in: Software Design


Leave a Reply