5.6 Composition versus Inheritance
Although OOP languages offer code reuse through inheritance, inheritance somewhat reduces the flexibility of the designed classes. This is especially true in languages such as Java and C#, which allow only single root inheritance (classes can extend only one class). You must take care in deciding which class to inherit from. Also, inheritance does not easily lend itself to versioning. Composition is the technique of building on existing implementations rather than inheriting from them. To use composition, you place a reference to the implementation inside your implementation.
Suppose you want a special kind of data structure, such as an LRU (least recently used) cache. The System.Collections.ArrayList class comes to mind. But should you extend it? Or should you use it internally to achieve the list-based functionality? Extending the ArrayList through inheritance means that your cache must be a list-based structure. So in the future if you want to extend it to an Array or a Map, you will have to rewrite the cache class logic. However, wrapping the ArrayList and providing wrapper methods that in turn make method calls to the ArrayList would insulate the end user from the internal implementation of your cache class.
Using inheritance, you could create the LRU class as follows:
class LRUCache : ArrayList {
public object Add(object o) {
base.Add(o);
}
}
Using composition, the LRU class looks like this:
class LRUCache {
ArrayList list;
public object Add(object o) {
list.Add(o);
}
}
Composition also gives you better control over the API you want exposed for your class. Extending an ArrayList would mean providing implementations for all the ArrayList methods. Instead, if you wrapped the ArrayList, you could expose the methods of your API in installments when the need arose. (For example, you don't provide a Remove method on your cache initially but instead provide it later when you work out the details of implementing it.)
 |