5

I was having a discussion with few friends about inheritance and composition and what I learnt from that discussion is that the use of inheritance more or less condemned in industry nowadays and either replaced with composition or copy and paste.

Doing a quick Google search confirms this with many articles with titles such as "X reasons to use composition over inheritance", "Avoid inheritance"...

I learnt one way to achieve polymorphism is through inheritance, if object A and B has a "is-a" relationship. Everyone have see that classic example of Shape, Rectangle extends Shape and so forth.

Class Shape {
    private String name;
    private double area;

    public double getArea()
}

Class Rectangle extends Shape {

}

In this example, they preferred to use composition or just copy and paste if there is only couple of fields being shared or child classes. I understand the advantages of using composition and why it can be powerful and better design pattern compared to inheritance. For a simple scenario as above, with no deep polymorphism, what are the advantages of composition or copy and paste over inheritance?

  1. Why does inheritance have so much hate in OOP and industry?
  2. Is there a valid case to use inheritance noadays?
  3. When is it considered a bad desgin pattern to use composition over inheritance?
  4. Java source code is full of inheritance and deep inheritance. Does it mean Java source is poorly designed?

Sorry for all these questions, I am trying to understand why the industry condemn using inheritance and when it's valid to use it.

9
  • 5
    Inheritance is perfectly fine to use. In your example there is no reason you should use composition. A rectangle is not composed of a shape, it is a shape. Composition is useful when your classes are standalone. A good use case for composition is in React. By using composition it allows you to create components which are both cohesive and reusable.
    – Jessie
    Commented May 29, 2018 at 4:36
  • 1
    They prefer copy/paste over inheritance? Wow... See a similar qiuestion on stackoverflow: stackoverflow.com/questions/269496/inheritance-vs-aggregation/… Commented May 29, 2018 at 5:23
  • @user2896976, I disagree; inheritance is almost never fine to use. Sure, we can dream up simple ideas like shapes to demonstrate how it works in theory. Out in the wild though, when modelling complex reality, it turns into a dogs dinner all too quickly. The answers to the duplicate question explain this in details.
    – David Arno
    Commented May 29, 2018 at 5:44
  • @Toon Krijthe - yes, they would rather copy and paste the class Shape had only them 2 fields and that single method.
    – Diaufh138
    Commented May 29, 2018 at 6:28
  • 10
    "have so much hate in OOP and industry" - I don't consider someone who thinks in terms of "hate" about tools like composition and inheritance as a professional. If I meet someone who is telling me he "hates" or "loves" a term from software engineering, I question his rationality. And so should you. Composition and inheritance are just tools, and inheritance has often beeing overused and wrongly used in the past by many devs. That does not mean it is not useful, it means you have to learn how and when to use it correctly.
    – Doc Brown
    Commented May 29, 2018 at 6:28

4 Answers 4

29

TL;DR

Attempting to use composition first, before attempting to use inheritance, prevents naive mistakes. Also, Class Inheritance:

  • Is easy to misuse.
  • Is less versatile than composition.
  • Is less versatile than interface inheritance.
  • Requires more maintenance than composition.
  • Provides some good guarantees about the derived types. Consider if it is worth the points above.
  • Should be used when we need to follow the Liskov substitution principle.
  • Can be necesary to extend poorly designed third party systems. Blame the third party.

Also, Java is not perfect.


Composition over Inheritance

Why does inheritance have so much hate in OOP and industry?

I think "hate" is not a fair representation. You will find lots of advice towards using composition over inheritance, because:

  • Composition is much more versatile.
  • Inheritance is easy to abuse, and beginners could avoid many naive mistakes by trying to use composition first.

Below are examples where a developer may want to solve the problem with inheritance, but it is not the best option.


Not exactly "is-a"

For example, sometimes people will use inheritance for things that fail the "is-a" relationship. Well, rather, I should say that fit much closer a "behaves-as" relationship. For example, in a video game I would have a lot objects that are visible, that I need to make sure to render them properly. It can be tempted to say that - for example - "Enemy is-a Visible", thus creating a Visible class. However, that is wrong. The correct thing to do is to make an IVisible interface.

In video games, sometimes you will find a push to say, "Enemy has-a Visible component". That has some advantages, for example, if I want to create an enemy that can turn invisible. Note: Yes, I could have a bool IsVisible and check, but if I have a list of Visible, and I remove it, I do not have to check at all.


A green car is not another type of car

A “Green Car is-a Car”, but it is impractical, ill-advised, and just plain wrong to create a GreenCar class when we can have a color attribute. That way, we do not need to create a new class every time we need a new color. Also, with inheritance, how do you change color? That is composition over inheritance.

Addendum:

Perhaps, you have another class, let us say: Building. And just like Car, Building also has a color. Should you create a ColoredThing base class? No. You should not. Why? Well, what if both Car and Building also have an owner... should it be OwnedThing then? Nope.

You see, if you abstract any common attributes, you will end up with a badly designed inheritance tree, and wanting to use multiple inheritance where it is not solving any real problem.

What if you need a list of things that have color? Should you use a ColoredThing class then? No. You should use an interface.

Why? Again, what do you do when you also need a list of OwnedThing? - It is about being open to extension. Interface is better because it does not narrow the possibilities to extend the system as much as a class does.

Is that what you call copy and paste? I would argue that is not copy and paste.

Ok, there is an argument for not repeating yourself there... One day you will discover that cars and buildings do not have a single color, they have multiple! Now you have to add more colors to all the things... I will grant that is bad, you should be able to change these things in a single place. No matter, changing the attribute of a type is easier than replacing a base class... Replace all your color attributes with a Coloring class, and your Coloring can have multiple colors.


From Multiple Inheritance to Component Systems

For example, if in the video game, the "Avatar is-a Soldier" (can use guns and shoot stuff). And in addition, the "Avatar is-a Pilot" (can drive vehicles), it can be tempting to use multiple inheritance.

Leaving aside that we have plenty of languages without multiple inheritance...

How do you model the Avatar learning a new ability? Under this model, you could have bool flags. The Avatar is created being a disabled Pilot. The more abilities you want to program in the game, the more stuff like that you need to add.

Multiple inheritance will become hard to maintain. Using interfaces will lead to code duplication, also hard to maintain. Use composition: the "Avatar has PilotAbility".


As you can see, composition is in particular more versatile than inheritance in languages without multiple class inheritance. Futhermore, I would argue that many languages do not have it by design, not by oversight. Multiple inheritance is easy to misuse, so it is restricted to the sane cases (interfaces), and we have alternative for other situations.


When to use Inheritance

Is there a valid case to use inheritance noadays?


We have - at least - three elephants in the room. I will quickly dispose of two of them and acknowledge the third...

  1. You do not need inheritance. Evidence is that some languages just straight up do not have it, and they are perfectly usable. However, inheritance can be convenient.

  2. Sometimes, you need to work with a system that require inheritance for extension. In that case you need inheritance, because the design of that system has narrowed your options. However, it does not imply that such system (or an alternative one that serves the same purpose) was impossible without inheritance. Why did they choose inheritance? Convenience. Note: I will shove down creating custom exceptions here.

  3. Interface inheritance is inheritance. However, I will be dealing with interface inheritance as if it were something else. Note: the advent of Default Methods removes some use cases of class inheritance.

Note: inheritance sometimes is an optimization, I will not be talking about that, I think optimization as an argument for inheritance is too weak. However, I want to mention that UI and widget libraries often take advantage of the fact that calling a virtual method can perform better than callbacks, listeners, observers, continuations, et. al. In fact, if your language does not support function references (Java, for example), using inheritance and calling virtual methods will be the most efficient way to delegate and notify.

Thus, we will not be talking about the need of inheritance, Instead we will be talking about the convenience.

With that said, yes, there are valid use cases.


Liskov substitution principle

You should be able to use the derived type everywhere you can use the base type.

Sometimes people would resort to inheritance, and then have to hide, set to null, or throw exceptions in members they do not want. In that situation is better to use composition. Unless we are extending a third party system, and inheritance was the only option (because we need virtual methods or we need to call methods that expect the base class), blame the third party.


I did a search in my code base, discarded all the cases where I had to use inheritance to extend a third party system, discarded all the cases that I would do differently with default methods, and arrived to the following example:

Consider a library that has a SortedSet type and that has a method which allows creating a SubSet from it. These subsets are not like the base type (they do not actually store the items), instead they are a view defined by a range and a reference to the set of which they are a subset. However, a subset "is-a" set.

In this case, I will have another type that actually stores the items, and SortedSet will have a reference to it (composition). Now both the SortedSet and the SubSet would have a reference to the same storage object. The SubSet type also has other two members that define the range. There are also virtual methods in SortedSet that SubSet will override (for example to allow you to take subsets of the subsets).

What is important here is that the SubSet from the point of view of the consumer is a SortedSet. In fact, I can make my SubSet class private and only expose it from SortedSet, as a SortedSet, without any loss of functionality (and I have done so).

Now, why was it important to use inheritance instead of composition? Because the derived class should be usable everywhere the base case class should be useable (Liskov substitution principle).

Why did I not use interfaces? Because the implementation of the subset is very tight to its parent, they share a lot of code, and are aware of each other (in fact the parent instantiates the derived class). Furthermore, you do not need to know they are different classes, at all, to be able to use them. While if I had used an interface, you had to remember to define your parameters as the interface instead of the class to be able to allow both.

Note: The SortedSet also implements an ISet interface. Moreover, yes, they are all generic.


Inheritance as guarantees

Inheritance, when combined with proper use of virtual and sealed(.NET)/final(Java) members, means that you have a tight control over the implementation, and thus you can trust it.

You will find this useful in scenarios where you deal with external resources, which you would need to pass around to allow extension. If the derived class can use the external resource in ways that would screw up the system, then we do not want everybody using derived classes that mess with the system.

In these situations, you would create an inheritance tree with the derived classes you might need. However, if designed properly, the consumer of the library can use them in composition, without the need of inheritance.


Bad composition

When is it considered a bad design pattern to use composition over inheritance?

Breaking the Liskov substitution principle

When you intend the types are replacements. By wrapping a type instead of inheriting from it, I would not be able to use my new type in all the places where I can use the base type. Thus, if I intend my type as a replacement, I should be using inheritance.

Notice that inheritance and composition should work together. For instance, thanks to composition, I can remove or change the components of an object, and thanks to inheritance, I can use replacements of different types.


Java

Java source code is full of inheritance and deep inheritance. Does it mean Java source is poorly designed?

I read this as an Argument from authority in disguise: If Java uses inheritance, and the people that created Java are smart, it must be smart to use inheritance.

Well, hold your horses...

First, let us be clear on that Java is - at least - three things: the language, the standard library and the virtual machine. I know you ask about the standard library. Soon it will make sense why I point this out.

Now, Java is not perfect. Some aspect of its design show its age. Sure, we have deprecations, but some things are too deeply rooted in Java that deprecation is not feasible. I will come back to the archaic parts of Java. First, let us see what parts use inheritance appropriately.

Appropriate use of inheritance

Disclaimer: I do not know the standard library by memory, or ever did. In fact, it has been a while since I used Java for anything serious. Thus, I am looking at this: Hierarchy For All Packages.

Using inheritance for exceptions (Throwable) and enums, is the state of the art.

There are things that interface with external resources, such as UI and database related types. I will give these a pass.

I see some threading related types use inheritance. This makes sense, just as with external resources, inheritance provides useful guarantees. We can make a similar argument for reflection.

Archaic but justified

The abstract data structures probably should be interfaces (and perhaps accompanied by utility classes). I am aware that these abstract data structures have usable common code, and thus simply making those interfaces will not do. In addition, default methods did not exist back when those were created (default methods required improvements to the language - and probably the virtual machine), and Java does not have something like extension methods. Therefore, they are archaic but justified.

I would argue that the whole listener model is a bad, at least for modern standards. The thing is that Java lacks a way to reference functions (virtual machine limitation). Therefore, they are stuck with this. Before Java had anonymous classes (language improvement), you would rather implement EventListener in your class. That was preferable to creating a bunch of small classes to implement each event type and solving what members you need to share and how to do it without breaking encapsulation. In the implementation of EventListener you would need to sort out the type of events, requiring to match the type of the EventObject. Thus, we need many sub types of EventObject. Therefore, this is archaic but justified.

The lang visitors, the text formats and other stuff that require some form of iteration, or work on strings or binary streams relies on inheritance. Considering that listeners are a pain (and there were nothing like the Stream API) it was better to use inheritance. Thus, these are all in the same boat: archaic but justified.


Other notes

Copy and Paste

Copy and paste is the second oldest form of reutilization (being transcription the first one). With that said, we would rarely, (in fact, I would argue, we would never) advocate for copy and paste.

In the example:

Class Shape {
    private String name;
    private double area;

    public double getArea()
}

Class Rectangle extends Shape {

}

It is not clear why you need a Rectangle class. I would argue that you could work with Shape only since Rectangle is not adding anything. If Rectangle will be adding more stuff, then inheritance would be useful here.

they would rather copy and paste the class Shape had only them 2 fields and that single method

No, please, do not copy and paste just to avoid inheritance. The argument is between inheritance and composition: Should you say that the "Rectangle has a Shape"? Refer to the Liskov substitution principle, if Rectangle is meant to be used where Shape is used, use inheritance.


On method overloads

If I create a method that accepts type T, it will also accept any object of a type derived from T. If you cannot modify this class, and you need this method, you will have to inherit from T. Now, the suggestion is to be as permissive as possible with what you accept, and usually means using an interface that closely resembles how the method will be using the object.

If we are working in a dynamically typed language, there is no method overloading and we will have to interrogate the objects, but that is another thing.

See Liskov substitution principle and Interface segregation principle.

5
  • 1
    Thank you for the detailed feedback and taking the time to answer all of my questions.
    – Diaufh138
    Commented May 30, 2018 at 0:24
  • Inheritance is a way of programming, specifically useful to use to avoid redundancy. It need not to be consider an is a. It can just provide functionality. I think this old school way of thinking is flawed. We get to inherit functionality, and override anything. You can simply add functionality by implementing an interface in your class and thus your class or object gets that power. I think since the world started using composition every code base just got very very redundant. Copy and paste everywhere due to lack of reusability and tweak ability / overridability. I sure prefer inheritance.
    – mjs
    Commented Aug 17, 2022 at 14:22
  • People that prefer composition typically just like to write the minimal amount of code that works for the specific task at hand, never splitting, unifying or grouping behaviour or features into inheritable classes or interfaces. The example of the Rectangle is your example, where you argue that the Rectangle provides nothing. Yet. The lack of foresight is always evident with people that like composition / redundancy. Next time, they just copy and paste your shape into the rectangle, because they need to calculate the area. The inheritance pattern is a pull common code up to unify.
    – mjs
    Commented Aug 17, 2022 at 14:27
  • If you like and prefer pretty and well organized code you sure as hell can not pull that off with composition. It will just lead to way more complex code than need be which is difficult to maintain. With inheritance, technically you can design advanced features that are pluggable. Now, with the Java language you can only extend one class, but using interfaces, default methods and soon likely instances ( I already can ), and hopefully also protected and package private visibility, you could do some crazy stuff where all your class will just simply get power by inherting an interface.
    – mjs
    Commented Aug 17, 2022 at 14:29
  • Problem is Java, and not the ideology or theory of inheritance. With a properly designed language, where you can extend as much as you want, composition would be dead, and should be. You can pull of much more maintainable system relying on inheritance that are way less complex and grouped into small units of code / classes / interfaces.
    – mjs
    Commented Aug 17, 2022 at 14:30
1

“The industry prefers” is a completely wrong view of the situation. The correct view is “the industry has figured out that composition is often better than inheritance, and as the better solution should be preferred, composition is often used instead of inheritance. The industry still prefers inheritance in those cases where it is the better solution. “

1
  • 2
    There is also a very big correlation with "the industry standard languages happen to have a very rigid and inflexible concept of inheritance that intermingles the orthogonal aspects of subtyping and differential code-reuse". A lot of the problems with Java-style inheritance go away if you don't have Java-style inheritance! Commented May 29, 2018 at 10:42
0

It is not considered bad design to use inheritance but composition generally creates more flexibility at the cost of more classes.

I generally use inheritance by implementing the Template Method pattern rather than creating any kind of hierarchy.

1
  • At the cost of more redundancy.
    – mjs
    Commented Aug 17, 2022 at 14:20
-2

First, negative and controversial messages tend to be louder and draw more attention then positive messages. Both people who agree and disagree would jump on it. Google is sensitive to that.

Applying inheritance for the wrong reasons will bite you in the ass soon. And this is a good thing. Some people who do choose inheritance for the wrong reasons and get in trouble tend to conclude the technology sucks rather than they did not think things over well enough.

Composition and copy-paste on the other hand will always go a long way. You will not be bitten because you basically start over with every new class and your application will gradually grow into an unmaintainable mess without much guidance for newcomers to the code base. Not your problem though.

Inheritance provides a rigid structure, strong guidance. This is restricting and often that is good. It is not particularly flexible. Also often good. But this also means it can get you stuck if you made the wrong decision somewhere down the line. So you should just use it wisely.

Not the answer you're looking for? Browse other questions tagged or ask your own question.