Taking Another Look At Inheritance

In my first lesson on Object Oriented Programming (OOP) I was taught how amazing inheritance is for code reuse, and object classification. Very excited and with high expectations, I set off with this new concept from my OOP toolbox, using it as at every possible opportunity. As time went by, I realized all is not well and that inheritance has a lot of subtle and unanticipated implications to a system’s behavior and implementation, beyond the quick class definition. Inheritance often initially looks like the right solution, but only later does it become apparent that there are some unintended consequences, often when it’s too late to make design changes. I hope to provide the full story of inheritance here, and how to make sure that it has a happy ending.

Let’s start with the basics: Inheritance is a classification where one type is a specialization of another. The purpose of inheritance is to create simpler reusable code, by creating a common base class that shares its implementation with one or more derived classes. Be forewarned inheritance needs to be very carefully planned and implemented, otherwise you will get the reuse without the simplicity.

Deciding to use inheritance or not consists of the following steps:

  1. What is wrong with inheritance?
  2. When to use inheritance, and when to avoid it?
  3. If inheritance is not used, what alternatives are available?
  4. If inheritance is used, how should it be used?

Problems of Inheritance

So what is wrong with inheritance? The biggest problems of inheritance stem from its fragile base classes and the tight coupling between the base class and its derived classes.

Fragile base class

The fragile base class describes a problem that occurs when changes to a base class, even if interfaces remain intact, break correctness of the derived classes that inherit from it. Consider the following example:

1.

 public class Employee
 {
 protected double salary;
 protected virtual void increase(double amount)
 {
 salary += amount;
 }
 protected void receiveBonus(double amount)
 {
 salary += amount;
 }
 }

public class Contractor: Employee
 {
 protected override void increase(double amount)
 {
 receiveBonus(amount);
 }
 }

Next a developer updates class Employee as follows:

2.

 public class Employee
 {
 protected double salary;
 protected virtual void increase(double amount)
 {
 salary +=  amount;
 }

protected void receiveBonus(double amount)
 {
 // salary += amount;
 increase(amount);
 }
 }

public class Contractor: Employee
 {
 protected override void increase(double amount)
 {
 receiveBonus(amount);
 }
 }

The modification made to the base class Employee in step 2, will create an infinite recursive call in class Contractor, without any modifications to Contractor. This problem will silently be introduced into the system that uses Contractor, and only manifest at run-time when it causes memory problems on the server. In a nutshell this is referred to as the Fragile Base Class problem of inheritance. It is also the biggest problem that occurs when using inheritance to share functionality.

Tight Coupling and Weak Encapsulation

Encapsulation, also referred to as information hiding, is one of the pillars of good OO design. It is a technique for reducing dependencies between separate modules, by defining contractual interfaces. Other objects depend only on the contractual interface, and the module can be changed without affecting dependent objects, as long as the new implementation doesn’t require a new interface.

Base classes and derived classes can access the members of each other:

  • The derived class uses base class methods and variables.
  • The base class uses overridden methods in derived classes.

When base and derived classes become dependent on each other to this extent, it becomes difficult and even impossible to make changes to one class, without making corresponding changes in the other class.

Inheritance best practices

Implement “is a” through public inheritance

A base class constrains and influences how derived classes operate. If there is a possibility that the derived class might deviate from the base class’s interface contract, inheritance is not the right implementation technique. Consider using interfaces and/or composition under these circumstances.

Design and document for inheritance or prohibit it

Together with the ability to quickly write reusable code, comes the risks of adding complexity to an application. Therefore either plan for inheritance and allow and document it, or stop its use entirely by declaring a class as final (Java) or sealed (C#). This might not be always a plausible solution, when certain frameworks, like NHibernate for instance, forces you to declare persisted class members as virtual.

Adhere to the Liskov Substitution Principle

In a 1987 keynote address entitled “Data abstraction and hierarchy”, Barbara Liskov argued that you shouldn’t inherit from a base class unless the derived class is truly a more specific version of the base class. The derived class must be a perfectly interchangeable specialization of the base class. The methods and properties inherited from the base class should have exactly the same meaning in a derived class. If LSP is applied, inheritance can reduce complexity, because it allows you to focus on the general role of the objects.

Avoid deep inheritance hierarchies

The amount of complexity introduced into an application using deep inheritance hierarchies, completely outweighs its benefits.  Inheritance hierarchies more than three levels deep, considerably increase fault rates. Inheritance should improve code reuse and reduce complexity. Consider applying the 7+-2 rule as a limit to the total number of derived classes in a hierarchy.

Specify what you want derived classes to inherit

Plan and control how derived classes can specialize a base class. Derived classes can inherit method interfaces, implementations or both. Abstract methods do not provide a default implementation to derived classes. Abstract methods only provide an interface, whereas virtual/overridable methods provide an interface and a default implementation. Non-overridable methods provide an interface and default implementation to derived classes, but derived classes cannot replace the inherited implementation with their own.

Don’t override a non-overridable member function

Don’t use the same names of private/non-overridable methods, properties and variables from the base class in derived classes. This is to reduce the likelihood that it might seem like a member is overridden in a derived class, but it is only a different member with the samem name.

Move common interfaces, data, and behavior as high as possible in the inheritance hierarchy

The higher interfaces, data, and behavior is moved up in the inheritance hierarchy, the easier it is for derived classes to reuse them. But at the same time don’t share a member with derived classes if it is not common among all the derived classes.

Don’t confuse data or objects with classes

There shouldn’t be a different class for every occurrence. For instance there should only be one Person class, and objects for Jack and Jill, and not a Jack and Jill class and one object of each. The warning signs should go off when you notice classes with only one object.

Be cautious when a base class only has a single derived class

Don’t use inheritance if there isn’t a clear need for it at the moment. Don’t try to provide for some future extension, if you’re not 100% sure what those future needs are. Rather focus on making current code easy to use and understand.

Be cautious of classes that override a base method with an empty implementation

This means that the overridden method isn’t common among all the derived classes, and that it is not suitable for inheritance. The way to solve this is to create another class that contains the implementation, and reference it from the derived classes that requires it (composition).

Prefer polymorphism to extensive type checking

Instead of doing several checks with if or case statements for an object’s type (typeof(Class) == object.GetType()), consider using derived classes that implement a common interface. This frees you from determining what the object is so that you can call the correct method.

Be cautious of protected data

When variables and properties are protected, you loose the benefits of encapsulation between base and derived classes. Allowing derived classes to directly manipulate protected data members, increases the likelihood that the base class will be left in an unexpected state, and create an error in the object.

Alternatives to Inheritance

Composition

In the terms of Object Oriented Programming (OOP), Composition, also referred to as Containment, is a fancy term that simply means a reference contained in another object. So instead of inheriting from the base class, you instantiate a new instance of the class whose functionality you need, and use that from the client class. Pretty standard stuff, but its implications are important when it comes to reusing functionality. Take the List class reused by the PersonList class using inheritance:


public class List<T> : IList<T>, ICollection<T>,
 IEnumerable<T>, IList, ICollection, IEnumerable
 {
 // ...
 }

public class PersonList: List<Person>
 {
 // ...
 }

Now take the same Lits<T> class reused by the PersonList class using composition:

 public class List<T> : IList<T>, ICollection<T>,
 IEnumerable<T>, IList, ICollection, IEnumerable
 {
 // ...
 }

public class PersonList
 {
 private  List<Person> person = new List<Person>();
 // ...
 }

The example demonstates the following:

  1. Inheritance is quicker to implement, but discloses the inner workings of the inherited class. Child class PersonList  already has all the methods available to store People. PersonList referencing List<Person> first needs to implement some public methods to make the private list usable to outside objects. The problem with the inheriting PersonList, is that all public methods and properties inherited from List<T> is also made available to outside objects. Since outside objects usually only require a small subset of these, you are providing less guidance on how your object should be used. Another way to look at it is to say you have a weak object contract. Hiding a private List<Person> variable behind a custom public methods provides a strict contract of how it should be used and hides any implementation details from outside objects. For instance should you wish to rather use a Hashtable, instead of a List, you can do so without worrying about it affecting any objects that are using PersonList. Inheritance is a little faster to use, but composition makes your code easier to use and last longer by reducing the impact of changes.
  2. It is easier (and safer) to change a method interface/definition of a class based on composition. Base classes are fragile and subclasses are rigid. The Fragile Base Class problem clearly demonstrates how base classes are fragile, when a change ripples through in unintended ways to derived classes, to eventually break an application. Derived classes are fragile because you cannot change an overrided method’s interface without making sure it is compatible with its base class.
  3. Composition allows you to delay the instantiation of the referenced object until required. This would be the case if the List<Person> variable in PersonList wasn’t immediately instantiated, but say only when it is called for the first time. With inheritance the base object is immediately instantiated together with the subobject. You do not have control over the base object’s lifetime.
  4. Composition allows you to change the behavior of an object at runtime. The referenced class can be changed while the application is running, without requiring changes to the code. This is especially true when interfaces are used to reference objects. When inheritance is used you can only change the base class by changing the code and recompiling the executable.
  5. Composition allows you to reuse functionality from multiple classes. With inheritance, the class is forced to reuse a single base class. Composition allows a class to use multiple classes. This also allows you to combine the functionality of several classes. For instance imagine an application with classes for different types of super heros. Some can create spider webs, others will fly like bats, and others will have retractable bone claws. Now image you can mix and match these to create the ultimate super hero, like one that can create spider webs, fly like a bat and have retractable bone claws. If you used inheritance you would be forced to choose only one type of super hero.

Interfaces

Interfaces are a great way to facilitate composition. You can almost say it is Composition By Contract (with interfaces you can build strong object contracts). When a class implements an interface it doesn’t get any functionality from a base class. An interface is only a contract telling objects what the class will do, but not providing any implementation to child classes. This means the Fragile Base Class problem does not affect classes implementing the interface, because there is no functinality flowing from the base class to the derived class.

Interfaces allow loosely coupled compositions, whereas inheritance and composition without interfaces can lead to tight coupling. Other objects bind only to an interface-contract and not the object implementing the interface. Therefore the underlying implementation is can be changed at any time during the application’s execution.

Patterns

There are a number of patterns that can be used as a more sophisticated replacement of inheritance. For now I will just mention them here, and maybe discuss each one in a future post:

  1. Strategy Pattern
  2. Part Pattern
  3. Business Entity Pattern

When to use inheritance and when to use composition

One of the most important goals you as a developer should have is to reduce the complexity of your application. Inheritance can quickly mutate from simple to a far reaching chain of complexity and confusion. Rather try to use composition/containment together with interfaces. You should therefore be biased against using inheritance, and only use it when you are absolutely sure that you cannot do without it:

  • To share common data, use composition by placing the shared data in a common object to be referenced by the other classes.
  • To share common behavior, inherit from a common base class that implements the common methods.
  • To share common behavior and data, inherit from a common base class that implements the common methods and data.
  • To allow classes to control their own interface use composition. Inherit if you want the base class to define the interface.

Inheritance hierarchies in a relational database

Relational databases do not cater for the concept of inheritance, and requires you to map your classes to your database schema (hence the term Object-Relational Mapping). Although there are a number of complications with mapping your classes to a database schema, because of an impedance mismatch, it should not stop you from using inheritance.

There are four strategies for implementing inheritance in a relational database:

  1. Map the entire class hierarchy to one table.
  2. Map each concrete class to its own table.
  3. Map each class to its own table.
  4. Map the classes to a generic table structure.

Map the entire class hierarchy to one table

In our Person example, using this approach you will create a table with columns for the combined properties for all the classes in the hierarchy, and an extra type column and boolean column to identity object’s type. Client, Employee and Contractor objects will all be stored in the same table. The table will have all the columns of the entire class hierarchy: Id, FirstName, LastName, Nationality, Company, Salary, and ContractTerm.

The main advantage of this strategy is simplicity. It is easy to understand, query and implement. Querying data is fast and easy because all the data for an object is in one table. Adding new classes is painless – all that’s required is to add the additional properties of the new classes to the table.

The disadvantages of this strategy are:

  1. Unconstrained use of tables with a growing number of empty columns. A child class will not use the columns of a its siblings. So if the hierarchy has a lot of child classes you end up with a table row with a lot of empty columns. A further problem is that it restricts the use of the NOT NULL constraint on columns, because a a column might be compulsory for one child class, but not the other, yet they share the same table. Therefore none of the child classes’ columns can have the NOT NULL constraint.
  2. Increased coupling of classes. All classes in the hierarchy share the same table, therefore a change to the table can affect them all.

Map each concrete class hierarchy to its own table

With this strategy you create a table for each concrete class implementation. Continuing with the Person example, you will create the following tables:

  1. Client, with columns Id, FirstName, LastName, Nationality and Company.
  2. Employee, with columns Id, FirstName, LastName, Nationality and Salary.
  3. Contractor, with columns Id, FirstName, LastName, Nationality, Salary and ContractTerm.

This strategy is also fairly simple and easy to understand, query and implement. It is simple to query beacuse all an object’s data comes from one table. Query performance is also good, because you don’t have to do multiple joins to get a single object’s data.

Maintenance and chaging a super class’s schema is a laborous process though. For instance, say you want to add Gender to the Person class? This means you have to update three tables: Client, Employee and Contractor.

Map each class to its own table

Mapping each class to its own table requires that the primary key of each child class also serve as the foreign key pointing to the related row in the parent table. The table of the class at the top of the hierarchy, in our case it’s Person, contains a row for every object in the hierarchy. The example will have the following tables:

  1. Person, with columns Id, FirstName, LastName, and Nationality. Id is not a foreign key, because Person is at the top of the inheritance hierarchy.
  2. Client, with columns Id and Company. Id is also a foreign key referencing the row in table Person that contains this object’s Person data.
    Id cannot be an auto incrementing or generated identity column because the parent row in the Person table will determine its value.
  3. Employee, with columns Id and Salary. Just as in the case of Client, Id is also a foreign key referencing the parent row in table Person.
  4. Contractor, with columns Id and ContractTerm. Just like Client and Employee, Id is also a foreign key referencing the parent row the Employee table.

To make it easier to identify what kind of object a row in the root table represents, you can add a PersonType table and PersonTypeId attribute to the Person table. This will make it easier to get the type of Person in queries, because you don’t have to go through several joins to get to the last child table that tells you what the object’s actual type is.

Map classes to a generic table structure

The last, and most complex, way to store your objects and their inheritance hierarchy is using a fixed table structure that can store any object. The following diagram describes how you would lay out your mapping classes and corresponding tables to accommodate any mapping, inheritance well as relationships :


The easiest way to explain this mapping meta-data engine is to use an example. Consider the following Employee object: new Employee { FirstName = “John”, LastName = “Doe”, Nationality = “ZA”, Salary = 11000 }. The Type table will have two entries one for the Person type/class, and one for the Employee class. ParentTypeId of the Employee record in the Type table references the TypeId of the base Person class. IsSystem is used to identity native system types, such as string and int. The Property table stores all the member properties and variables of a class, so Employee will have one entry for the Salary property, and Person will have 3 entries for FirstName, LastName and Nationality. This takes care of the type and mapping meta-data.

Object instance state is persisted as follows. For the Employee called “John Doe” there will be one entry in the Object table for the instance, and 4 entries in the ObjectPropertyValue table, one for each property. A Property can store a primitive type, like a string, in which case the Value column/property will contain the actual value. If a Property references another Object, then the Value column/property will contain the ObjectId of the referenced object.

The above solution provides the most flexibility. However, it requires a complicated meta-data mapping and administration layer. Understanding, querying and reporting data becomes a very painful and slow process. This technique is only included for completeness. If this level of flexibility is required, I highly recommend that you rather look at a dedicated Object Relational Mapper (ORM) like NHibernate or Entity Framework. As a side note; I have not applied this specific solution in real life, so consider it only as a proof of concept, and not a tried and tested implementation.

References

You can change the behaviour of each ball at runtime: a bouncing ball can become a fading ball at any time.
About these ads

6 Comments on “Taking Another Look At Inheritance”

  1. openlandscape says:

    I really should start writing shorter blog posts. With this one I thought it’s going to be a short-ish one, but inheritance turned out to be a massive topic.

  2. Seconded—and being sleepy I only managed to skim through it.

    In my experience, however, most problems with inheritance go away if one strictly differs between interface and functionality inheritance—and uses class and interface hierarchies only for the first (to the degree that is pragmatically possible). Meanwhile, functionality is best inherited through various forms of composition and containment (which you partially address). The decorator pattern, in particular, can be immensely helpful.

    • openlandscape says:

      Michael,

      I have to agree on that. You actually sum it up very nicely in one sentence. But even for interface-inheritance I believe interfaces provide the same benefits, with more flexibility. So this brings me back to one of the points I make, and that is basically use containment and composition by default, & inheritance only when really need it.

  3. Cody says:

    Hi,

    Awesome article, this has cleared up a lot of confusions I had. But in your Employee example I’m struggling a little to see how the Employee class is using bad practices (even though it’s obviously causing problems). Like, I definitely would not have looked at the original class and said “that’s prone to error”; it looks fine to me before it’s subclassed!
    Is there any particular guideline that this class doesn’t adhere to, or should this stuff just be handled on a case by case basis?

    • openlandscape says:

      Apologies for the late reply, like all good developers, I’m completely swamped in work ;-) :-)

      Also, it’s actually good you’re asking this, ’cause you’re forcing me to reread my own post again… yeah, it’s been a while. Anyways, I think in terms of specific problems that occur from using inheritance, I think you’re right, you will just have to go through each step in the execution process and check if anything funny’s happening. This is very time consuming AND might not even be possible. For instance, what happens if Contractor was in a different dll, from someone outside your organization?

      Therefore, my best practice is actually very simple: Use inheritance VERY lightly. A base class with some simple functionality, and one child level below it. Anything more than that, and I really start questioning my design. By default, try to use composition, and if it really feels wrong, or doesn’t seem like a good fit, then use inheritance. So sorry, no, I can’t think of any specific best practices, other than the mentioned general advice.

  4. […] Taking another look at classical inheritance […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.