Effective Use of Inheritance

If inheritance is used in a proper way, it can reduce the code duplication and enhance code reuse. The following conditions must be satisfied for proper use of inheritance: Inheritance should be used only when a derived class is a specialized kind of base class. Since the base class places constraints on the behavior of the derived classes, if the derived class cannot operate within the constraints then inheritance cannot be used.

Apply Liskov Substitution Principle (LSP). We must be able to substitute the subclasses wherever we can use base classes and the user should not find any difference. The means that all the methods defined in the base class should have the same meaning when they are used in any of the derived classes.

Consider a base class of Toy and derived classes of SpiderManToy, SuperManToy and BatManToy. We should be able to invoke any of the methods derived from Toy on any of Toy's subtypes without knowing about any specific subtype of Toy. Following LSP allows us to manage complexity since we can ignore the details and focus on the generic attributes of an object.

If a developer has to know the details of the subclass in order to use it then we have increased the complexity instead of reducing it. For example, if a developer has to think as: "If I call the getAccountBalance() method on RegularCustomer it returns the amount of cash that the customer has on hand but if I call getAccountBalance() on DefaultCustomer I have to make the result negative because the customer does not have any cash on hand and has accumulated charges in teh account that is owed to the bank". LSP states that DefaultCustomer should not inherit from Customer base class because the semantics of the getBalance() method in DefaultCustomer is different from the semantics of the getBalance() method in Customer. Inherit only what you need. A derived class can inherit interfaces only or implementation and interface.

Let us consider the scenario of inheriting interface only. A Java class implementing an interface is an example of interface only inheritance. It is called as sub typing. Whenever the LSP principle is satisfied by a class, it is said to be a sub-type. This can also be accomplished by extending an abstract class with no default implementation for any methods. The intention might be to inherit only the attributes and not the implementation.

In interface and implementation inheritance, the derived class inherits the interface and a default implementation (could be allowed to override)

If you want to use the implementation of a class but do not want to inherit the interface then use containment (by delegation) rather than inheritance. The class that uses delegation could be a wrapper around the object which does the actual work.

Inheritance when properly used minimizes the code duplication as well as complexity.

Inheritance Vs Delegation

Delegation can be used as an alternative to inheritance. It is the ability of an object to fulfill a responsibility by issuing a message to another object in response to the receipt of a message. It can be implemented with an association or aggregation to another object. An operation on the first object invokes an operation on the second object to accomplish its work.

Apart from the above inheritance test, we can use the following design guidelines to decide when to use inheritance over delegation. These guidelines will be explained in more detail later in this chapter.

Data Behavior Design Guideline
Yes No Create a common object that the classes can contain (delegation)
No Yes Inherit the classes from a common base class that implements the common behavior
Yes Yes Inherit from a common base class that defines the common data and methods