Comments

What text books tell you about inheritance in OOP is wrong

I received an email from one of my students asking for a design advice. He is working on an application used by a sports team manager, where they can create, edit and delete players, teams and coaches. So, the design that my student had in mind is something like this UML diagram:

UML

So, a SoccerPlayer is a Player, which in turn is a Person. This is what many text books on object-orientation programming teach you:

When you can express the association between two concepts using IS-A, that’s an inheritance!

Many of these books claim that by creating these abstractions, in this example Person, you can “re-use” them in other projects. However, in 15 years of my professional experience, I have hardly seen many (or any) examples of such abstractions being used across projects in a useful manner.

At one point, I started creating a class library of these abstractions that I could share across projects. After a little while, that class library ended up being bloated with lots of unrelated abstractions and versioning it across different projects ended up being a nightmare. If you have done this before, you may relate to this story.

An extreme example of inheritance

Once I was engaged as a consultant for a greenfield project, and in the introductory session, the data architect of the project walked me through tons of UML class diagrams (more than 50 pages). Every class was inheriting from another class and eventually, they all led to a class that was called “Thing”! No joke!

Interestingly, there was not even one behavioural diagram expressing the behaviour of the system. So this huge inheritance hierarchy was only for inheriting “attributes”, not behaviour, which is what inheritance is all about!

Guess what? After spending more than a year and half a million dollars on that project, the company decided to shutdown the project. Nothing was produced, not even a beta version! They engaged various consultants and teams and no one could implement an application with such a large complex model.

Does this story sound familiar to you?

Don’t model the universe!

When building software, you should design a domain model for an application, based on the needs of that application and not the reality. The use cases that the application is going to fulfil will determine the angle at which you should look at the domain model.

In the real world, a SoccerPlayer is a Player and a Player is a Person. But just because you can express such relationship in English, doesn’t mean you should add such association between your classes. You’ll end up polluting your model with lots of unnecessary complexity that serves no purpose.

Inheritance increases coupling in your design

So what is wrong with inheritance? Well, before explaining, let me clarify something. I’m not anti-inheritance! Inheritance, just like every thing else, has its uses. When you use it in the right context, it works for you. But if you abuse it, it leads to increased complexity in your applications.

Inheritance creates tight coupling between your classes. If class Child derives from Parent, it is tightly coupled to the Parent. If you make any changes to the Parent, you may have to modify the Child as well. Or at a minimum, you need to re-compile it and re-deploy the assembly in which it is defined.

Now, when you have a small hierarchy with a few classes, that is not an issue. But as your hierarchy grows, the impact of change grows in your application. The further on top of the hierarchy you make changes, the more classes may be affected or at least need to be re-compiled.

When to use inheritance

So, when should you use inheritance? When re-using behaviour and potentially overriding it, which leads to polymorphism. But even then,  you could use composition to achieve the same thing with less coupling in your design. The famous saying favour composition over inheritance explains that. I’ll write a separate post on this topic soon.

Does this sound too abstract for you? Ok, continue reading for some simple pragmatic tips.

When not to use inheritance

I love theory in practice. If you’re the same, let me give you my super simple and pragmatic advice by showing you some code. If you have one or more of the following symptoms in your code, you probably don’t need inheritance. You can collapse your hierarchy, reduce the coupling and simplify your design.

When you have hollow classes

Do you have classes like this in your design?

public class Person 
{
     public int Id { get; set; }
     public string Name { get; set; }
}

public class Player : Person 
{
}

In case you’re not familiar with the C# syntax, here we have a Person class with two attributes: Id and Name. The Player class derives from Person.

The Player class here is what I call a hollow class. It serves no purpose. We could simply use the Person and reduce complexity in our design.

Here is a variation of this example:

public class Person 
{
}

public class Player : Person 
{
     public int Id { get; set; }
     public string Name { get; set; }
}

In this example, Person is a hollow class and is useless.

Here is the important part I want you to pay attention to: in the domain of this application, what matters is the Id and Name of each Player. The fact that a Player is a Person in the real-world does not matter in this application. And that’s what I meant by not modelling your applications based on the reality. Instead, you should model based on what the application needs and how it is going to behave.

When your inheritance hierarchy is all about attributes

Look at this example:

public class Person 
{
     public int Id { get; set; }
     public string Name { get; set; }
}

public class Player : Person 
{
     public byte Number { get; set; }
}

public class Coach : Person 
{
     public byte YearsOfExperience { get; set;
}

The argument behind this design is that we’re re-using the Id and Name properties in the Player and Coach classes. I used to have many classes like this before! The extreme example I mentioned earlier was exactly like this.

But re-using attributes is a poor way of thinking about inheritance. How much effort would be to duplicate these attributes in the derived classes? It would take only a second to copy/paste them. You may say but duplicating code is bad. Yes, but not always! It is bad if you’re going to change it and have to make the change in multiple places. But how often would these attributes “change”? Not often or never! How about the logic? Logic, algorithm and behaviour can change often.

That’s the reason that you should reserve inheritance for re-using behaviour, not attributes. But even then, you can use would better use composition.

Let’s sum it up

So, remember, inheritance increases coupling between your classes. Reserve it for situations when you want to implement polymorphism, not just for re-using code, especially attributes. If you have one or more of the following symptoms in your design, you probably don’t need inheritance:

  • Hollow classes that have no members
  • Base classes that include only attributes. They don’t have any methods for the derived classes to re-use or more importantly, override.

If you see this in your design, you can achieve a simpler, cleaner and more maintainable design by collapsing your inheritance hierarchy.

So, keep it simple!

 

If you enjoyed this post, please share it and leave your comments below.

 

Hi, my name is Mosh Hamedani and I am the author of several best-selling courses on Udemy and Pluralsight with more than 130,000 students in 196 countries. You can see the list of all my web and mobile development courses on this website.

Related Posts

Tags: , ,

33 responses to “What text books tell you about inheritance in OOP is wrong”

  1. Ale says:

    Great article! Tools like inheritance are used to simplify our code structure, but as you clearly show can be quite often result as an hinderance to that. Love how you cherrish producing the most synthetic and functional code possible!

  2. Vaibhav Salwe says:

    Excellent article!

  3. Ger says:

    I prefer to think along a different line. Player and Coach are not persons but Roles. We can also have another role, ‘Admin’

    Then we could have a PersonRole table where a person can have one or more roles. Assume in a small clup a person could hold all three roles!

  4. sinip says:

    Good points indeed. and as you said, the subject is thought like that in every single book about inheritance.

  5. Thyr says:

    Great article for beginners! The sooner you get best practices like this the faster you will become a good programmer.

  6. Dave says:

    Very good article. I’ve seen inheritance used so many times because “That’s how you’re supposed to do it”. This mentality really does turn projects into massive beasts with lots of waste.

  7. Tom says:

    Thanks for posting this blog post! It touches two things that we deal with on regular basis. Code that is too tightly tied to real-life or business workflows and inheritance where a HAS-A is more useful. What i found missing here are examples of good usage of Inheritance and Alternatives besides copy-pasting code.

    Again, thanks!
    Tom

  8. Pierre says:

    Concise, simple and elegant code is Mosh’s guideline, and this article is a brilliant reminder that reality and code don’t have the same paradigms. An application cannot reproduce our reality, because it creates its own based on its specific needs. Simple indeed, but we have to fight for!

  9. Bosko Loncar says:

    Thanks very much Mosh. I fell into this trap and it’s difficult to maintain, especially if you are persisting your classes with entity framework.

  10. Ot says:

    Great article!!
    Thanks for the info. The textbook descriptions do not always take practical considerations into account, especially the bit about inheritance increasing coupling in our design.

  11. Yes agreed, keep the code as simple as possible. No need to make unnecessary structures.

  12. Charles says:

    Thanks for the tip Mosh. Could you please flesh out your argument further and provide a comparison of inheritance versus composition? That would be helpful for us newbies.

  13. Very nice article…

    Actually, the idea of re-use in the object oriented world has been completely debunked by research quite a number of years ago (I wish I had kept the paper.). Research over many projects found that OOP did not encourage re-use at all, which was supposedly one of the primary tenets of the paradigm. In fact, it was found that just the opposite was found where multiple OOP libraries were being developed in companies, many being completely redundant.

    As to inheritance, the article is correct in stating that such implementations make the coupling of classes much tighter. It has also been found to make applications that incorporate inheritance quite brittle to maintenance.

    I believe it was Micrososft’s Tom Patton, the COM\COM+ guru, who wrote in his second book on the subject that it was found that most applications that implement inheritance experienced serious issues as a result of poor design and even poorer maintenance.

    The first major application to use inheritance in New York City was implemented by a financial firm on Wall Street. It was reported in the press as a literal nightmarish experience to develop, though worthwhile. However, that was back in the 1990s.

    My advice, if you don’t really need it than don’t use it. It would be much easier, as it regards the use-case in the essay, to simply make a “player” and “coach” classes containing the same properties as necessary. Yes, it is redundant but it is also far safer (in that the application is not exposed to faults in the inheritance hierarchy), more efficient, and much easier to maintain.

    Also remember, that OOP as paradigm is based upon theoretical constructs that were designed before being tested in the real world. Many times, the real world does away with such theoretical underpinnings because they are simply inefficient and\or they cannot be easily implemented due to real-world circumstances…

    • admin says:

      Loved your comment, especially the part about theoretical constructs that were designed before being tested in the real world. So true!!

      • Hugo says:

        Let’s not go overboard here, object-oriented C++ has been around and in successful wide use for more than 35 years. At this point none of it is theoretical. Constructs from C++ pretty much form the basis of what you see in more modern languages.

  14. Timothy Dooling says:

    While the point of the article is well-intentioned, I think the article misses the point that classes should be driven by data, and sometimes data from a database has many repetitive properties.

    If you are into writing .NET generics (and I am), writing a class hierarchy where the lowest one contains important things like the database id property means that I can write a generic class that can include LINQ methods that apply to any class that inherits from the base class.

    Also, the methods of the parent classes do not need to be repeated in the child classes unless there is a requirement that they be overridden.

    There is another rule of coding practice called DRY or “Don’t Repeat Yourself”.

    Coupled code is undesirable unless it involves a system of data that is inherently interrelated. Then it is a necessity because the relationships involving the data require it.

    So, while the article does make a valid point about promiscuous use of inheritance, when data is involved use of inheritance to reflect the natural relationships between data entities (usually tables in a database) is not inappropriate.

    Hollow or skeleton classes that inherit from a base class are appropriate in early design phases when fleshing the class out is anticipated in the future as the requirements of an application are more clearly defined. This is an appropriate practice in an agile design situation where there is regular feedback from the client.

    • Johann Gerell says:

      What you need is actually Role Interfaces. Since C# (my assumption of your situation, since you said .Net) only has single inheritance, you cannot get the base class attributes Id and Name from one base class and Address and PhoneNumber from another base class – you’re instead forced into one kind of hierarchy where every parent and child must have everything. With Role Interfaces, you can instead compose functionality of the 2 classes that provided Id+Name and Address+PhoneNumber into a concrete class that implements 2 role interfaces.

  15. Tomer says:

    In my opinion…you should use inheritance only when looking for polymorphism and only if you can guarantee it, or else your code will not be SOLID.
    The example with the ‘hollow class’ is actually not a good example ,in my opinion, because you assumed that the developer want to reuse the code(id, name).

    many developers thinks that inheritance is for code reuse, and this is the “mistake” in the text books

  16. Steve G says:

    There is a use case for attribute based inheritance: there are times and places where you wish to perform operations on your objects and (to extend your example) you A) don’t care if the object is Person or Coach, but B) you need to have one method or behavior operate on common attributes of both types of objects. For example, consider:

    void NotifyByEmail(Person p);

    Both Coaches and Players need to be notified, and the notification operation does not need to be aware of the difference between Coach and Player. While this particular example does seem to hark back to your early statement about behavior being the primary driver of inheritance, in this case it appears to me that driver of inheritance is that the IS_A relationship contains a common attribute (email address).

    And, just to complicate things, there are other ways to solve this issue. Common implementation of an interface such as “EmailRecipient” that contains an email address, for example – but then that’s still just a mock up for attribute based inheritance.

  17. Sije de Haan says:

    Inheritance can be very useful, but it has two disadvantages:

    1: A class can only be the descendant of only one other class.

    2: Sometimes the inheritance structure becomes quit complex. When there are many levels you sometimes have to search a lot of classes for a inherited method.

    These days I more and more use interfaces. A class can implement as many interfaces as you like and you are completely free to choose the constructor of each class.

  18. safder says:

    Very nice mosh well done try to post more topics like this

    Thanks Bro

  19. Mohammad says:

    That’s a great article, and as Timothy already mentioned an appropriate design typically comes with well defined hierarchy of classes.
    Inheritance is a fundamental part of object oriented designs and it’s a very powerful concept. With power always comes responsibility, overusing inheritance leads to coupled code.
    I found this article a little biased. I think this can be misleading for those who are new to object oriented design and they can fall from the other end which is not using proper class hierarchies and violating SOLID and DRY principals.
    For example in patterns like Visitor, Flyweight, Decorator, Template-method and many others inheritance is playing a great role and ‘object composition’ is not an appropriate replacement.
    Another thing is, the term “Favor ‘object composition’ over ‘class inheritance’.” (Gang of Four 1995:20) is bloated and extended to the extreme. It does not say “Use” rather “Favor”.

  20. Andrew Shirzad says:

    Inheritance is useful for reuse of code when modelling the behaviour of objects, true. But one thing that often gets overlooked when using inheritance is how to test and maintain the code.

    Using inheritance tends to cause duplicated test code, or test classes that mimic the inheritance heirarchy of the classes under test. In contrast well designed composition of dependent behaviour injected into the modelled objects leads to better tested code with less duplication and mess.

  21. Oswald Umeh says:

    This article will definitely touch some nerves. Some of the things stated in it are ok. While some I don’t agree with. The example used in section “When your inheritance hierarchy is all about attributes” is where I’ll take my bearing from.

    One of the goals of OOP languages is to enable programmers build software using concepts and terms prevalent in their day-to-day interaction with the world they live in.
    Some of these things are abstract (or conceptual) and others real. For example, the term “Car” is conceptual/abstract, where as a “Toyota Camry” is real and yes, a car as well. The best way to represent this in code is to use an inheritance hierarchy where “Car” is an abstract base class and Toyota Camry, derived.

    The above example is akin to the Person/Player one in the article where Person is abstract while Player is a real manifestation of a Person. Hence I see nothing wrong with that relationship even though the difference between both classes are just attributes, yet there is a difference.

    I want to add that a good understanding of the domain and its own abstractions in very important. A good grapple of OO design is very essential as well.

  22. Joe says:

    Actually, I couldn’t disagree more with this post. From my perspective, the problems most people gripe about in relation to inheritance usually occur because folks are not using inheritance correctly. The diagram displayed at the top of this article is a perfect example. The roles of “player” and “coach” are states (temporary roles these people will eventually leave), not a “type” of person. The details of operations specifically related to those roles should be implemented via polymorphism rather than inheritance and attributes specifically associated with those particular roles should be elements of composition.

    For example, the Person class might have a method called work() or doDailyActivity() but it could delegate to the role class (polymorphism) to provide the details of the actual work/activity performed during any given period.

    If derived classes of person were focused solely on attributes and operations which will be permanent aspects of these folks for all time there would never be a problem. Libraries only become bloated with unnecessary fluff when things are put in there that don’t actually belong. I know it’s hard to avoid because only hindsight is 20/20 but I do think that’s the fact of the matter. An argument could be made that a person may always have an attribute which we could define as a “role” (or even “roles”) but no derived class should be named based on a specific attribute value which will eventually change in the future. As I said, that is an example of state.

  23. Hugo says:

    The problem with this article is that the author concentrates on data structures without looking at how useful inheritance can be in the context of the code executed ON these data structures. If you have a base class Person, then you can use any derived class to run the method Foo(Person p);. There is no other way to accomplish this efficiently using composition. I guess you could define an IPerson interface, and then redefine your method as Foo(IPerson p);, but in that case you need to manually implement the same boring copy pasted code in all the classes that implement IPerson. Lots of duplicated code.

    The crux of the matter is it’s not one or the other. Inheritance and composition are almost always used together. And it’s not as simple as is-a vs has-a either. Inheritance is not bad. Long inheritance chains are.

  24. Ibraheem Al Saady says:

    this is a very interesting article. love it.

    I just wanted to see more about the usage of inheritance and best practices.

  25. Dhanasekar Murugesan says:

    Well said Mosh. Thank you for this article. It will help me to write cleaner code with less coupling.

  26. Manish says:

    Hi Mosh,

    Great article, it has simply broaden my thinking about inheritance.

    As mention in the blog, do you have a article on “Composition over Inheritance”.

    Looking foreword to hear from you.

Leave a Reply