When would I use the delegation pattern instead of inheritence to extend a class's behavior?

John Moore

Both delegation and inheritance are important concepts in object-oriented software design, but not everyone would label them as patterns. In particular, the seminal book on design patterns by the “Gang of Four” contains a discussion of inheritance and delegation, but the authors do not treat these topics as specific patterns. It is reasonable to think of them as design concepts which are more general than specific design patterns.

Inheritance is a relationship between two classes where one class, called a subclass in this context, inherits the attributes and operations of another class, called its superclass. Inheritance can be a powerful design/reuse technique, especially when it is used in the context of the Liskov Substitution Principle. (The article by Robert Martin at http://www.objectmentor.com/publications/lsp.pdf provides an excellent explanation of the ideas behind Barbara Liskov’s original paper on using inheritance correctly.) The primary advantages of inheritance are

  1. it is directly supported by object-oriented languages, and
  2. it provides the context for polymorphism in strongly-typed object-oriented languages such as C++ and Java.
But since the inheritance relationship is defined at compile-time, a class can’t change its superclass dynamically during program execution. Moreover, modifications to a superclass automatically propagate to the subclass, providing a two-edged sword for software maintenance and reuse. In summary, inheritance creates a strong, static coupling between a superclass and its subclasses.

Delegation can be viewed as a relationship between objects where one object forwards certain method calls to another object, called its delegate. Delegation can also a powerful design/reuse technique. The primary advantage of delegation is run-time flexibility – the delegate can easily be changed at run-time. But unlike inheritance, delegation is not directly supported by most popular object-oriented languages, and it doesn’t facilitate dynamic polymorphism.

As a simple example, consider the relationship between a Rectangle class and a Window class. With inheritance, a Window class would inherit its rectangular properties from class Rectangle. With delegation, a Window object would maintain a reference or pointer to a Rectangle object, and calls to rectangle-like methods of the Window object would be delegated to corresponding methods of the Rectangle object.

Now let’s consider a slightly more complex example. Suppose employees can classified based on how they are paid; e.g., hourly or salaried. Using inheritance, we might design three classes: an Employee class which encapsulates the functionality common to all employees, and two subclasses HourlyEmployee and SalariedEmployee which encapsulates pay-specific details. While this design might be suitable for some applications, we would encounter problems in a scenario where a person changes, say from hourly to salaried. The class of an object and the inheritance relationship are both static, and objects can’t change their class easily (but see the State pattern for tips on how to fake it).

A more flexible design would involve delegation – an Employee object could delegate pay-related method calls to an object whose responsibilities focused solely on how the employee is paid. In fact, we might still use inheritance here in a slightly different manner by creating an abstract class (or interface) called PayClassification with two subclasses HourlyPayClassification and SalariedPayClassification which implement classification-specific computations. Using delegation as shown, it would be much easier to change the pay classification of an existing Employee object.

This second example illustrates an important point: In implementing delegation, we often want the capability to replace the delegate with another object, possibly of a different class. Therefore delegation will often use inheritance and polymorphism, with classes of potential delegates being subclasses of an abstract class which encapsulates general delegate responsibilities.

One final point. Sometimes, the choice between delegation and inheritance is driven by external factors such as programming language support for multiple inheritance or design constraints requiring polymorphism. Consider threads in Java. You can associate a class with a thread in one of two ways: either by extending (inheriting) directly from class Thread, or by implementing the Runnable interface and then delegating to a Thread object. Often the approach taken is based on the restriction in Java that a class can only extend one class (i.e., Java does not support multiple inheritance). If the class you want to associate with a thread already extends some other class in the design, then you would have to use delegation; otherwise, extending class Thread would usually be the simpler approach.