How does inheritance lead to higher coupling than composition?

  softwareengineering

One major reason for using composition over inheritance is, that inheritance leads to higher coupling. How is that?
In both cases members are exposed to the subclass (in case of inheritance) or wrapper class (in the case of composition).
Compare the following modifications for the base class for inheritance (AnimalInheritance) and composition (AnimalCompositionInterface):

  • modifying the method name of #makeSound
  • adding parameters of #makeSound
  • changing the return type of #makeSound

In all cases the same refactorings have to be made in the subclasses (DogInheritance/DogComposition) or within the main() method, regardless if we’re dealing with inheritance or composition.
So why is inheritance considered high coupling?

//INHERITANCE EXAMPLE
abstract class AnimalInheritance {
    public abstract int makeSound();
}

class DogInheritance extends AnimalInheritance {
    @Override
    public int makeSound() {
        return 0;
    }
}


//INHERITANCE TURNED INTO COMPOSITION
interface AnimalCompositionInterface {
    public int makeSound();
}

class AnimalComposition implements AnimalCompositionInterface{
    @Override
    public int makeSound() {
        return 0;
    }
}

class DogComposition implements AnimalCompositionInterface{

    AnimalCompositionInterface animal;

    @Override
    public int makeSound() {
        return animal.makeSound();
    }
}


public static void main(String[] args) {
    DogInheritance dogInheritance = new DogInheritance();
    int dogInheritanceSound = dogInheritance.makeSound();

    DogComposition dogComposition = new DogComposition();
    int dogCompositionSound = dogComposition.makeSound();

}

11

It doesn’t have to.

The difference isn’t the required amount of coupling. It’s that with composition the coupling (delegation) is explicit. You have to write out what you’re delegating. With inheritance it’s implicit.

Inheritance is easier to write while composition is easier to read.

But these problems are small compared to the big problem. When reading through a procedure that lives in an inheritance stack you find yourself bouncing up and down levels of inheritance and getting lost. It’s called the yo-yo problem.

You don’t get that problem with composition for one simple reason. You can’t sneak inside your parent. You have to access what you’re composed of through it’s normal public interface like anything else would.

Which means you have the right to expect to work with an abstraction. Something that makes sense even if you don’t look inside. If you don’t have to look inside, no yo-yo.

3

Coupling between classes can only be lowered by using interfaces or abstract classes. The idea is to swap out the implementation of one of these, and the other classes that depend upon those interfaces will continue to work as before. Therefore, if class A uses an interface B, and you have classes C and D as implementations of B, A is not coupled directly to C or D. If A contains C or a D as concrete implementations, than A is coupled to C or D.

Following the same logic. If A inherits B, then you can’t swap B for another implementation, therefore there’s a high coupling between A and B (unbreakable one once you shipped a framework for example).

Therefore…

Composition leads to lower coupling if and only if the composing members are interfaces or abstract classes. Otherwise it is the same coupling. However, even if you do have concrete classes inside a composition, you still have the advantage of being able to maybe extract an interface or abstract class out of these. So if your class uses only interfaces to do its job, then you can swap the implementation of any of those interfaces, and there is no tight coupling between your class and any of the implementations.You can’t really do this with inheritance (some tools will consider inheritance as higher coupling because of this, but this is subjective). This is assuming SRP has be followed here. You can still create horribly coupled classes using composition. Simply pulling out some functionality out of your class in another class is not going to lower coupling.

There’s 2 basic rules that you can follow to figure out if you should use inheritance or composition.

  1. If class A is a specialization of B, then A inherits B (i.e inheritance). It is like a dog is a specialization of carnivorous animal.
  2. If class A needs something that B can do, or has a B, then B should be a member of A (i.e composition). A dog needs bones to move, but you can’t say a dog inherits bone, right?

Your example clearly demonstrates a high coupling with composition. The class DogComposition shouldn’t depend on the concrete AnimalComposition. It should depend on the interface AnimalCompositionInterface. To be 100% to the book, the instance of AnimalCompositionInterface should be passed as a parameter in the constructor. That way, if a class AnimalComposition2 comes to be, you can swap AnimalComposition and AnimalComposition2 without modifying DogComposition . Also DogComposition shouldn’t implement AnimalCompositionInterface. You basically implemented the facade pattern around AnimalCompositionInterface.

//INHERITANCE TURNED INTO COMPOSITION
interface AnimalCompositionInterface {
public int makeSound();
}

class AnimalComposition implements AnimalCompositionInterface{
    @Override
    public int makeSound() {
        return 0;
    }
}

class DogComposition{

    AnimalCompositionInterface animal;

    public DogComposition(AnimalCompositionInterface impl) {animal = impl;}


    @Override
    public int makeSound() {
        return animal.makeSound();
    }
}

public static void main(String[] args) {
    DogInheritance dogInheritance = new DogInheritance();
    int dogInheritanceSound = dogInheritance.makeSound();

    DogComposition dogComposition = new DogComposition(new AnimalComposition()); //you can swap implementations here
    int dogCompositionSound = dogComposition.makeSound();

}

The inheritance hierarchy is clearly coupled because the derived class uses the abstract’s class public method to do things. So DogInheritance is coupled to AnimalInheritance. You can’t swap AnimalInheritance for some other class with a different method to override.

EDIT #2

Since you updated the example to use the code above, the idea is basically that your composition is now coupled to the interface AnimalCompositionInterface and not to the concrete implementation of AnimalComposition. Since interfaces do not have actual implementation, this is considered acceptable coupling because you are essentially coupling to nothing. Or that you do not directly couple DogCompsotion to AnimalComposition, but use an intermediary instead. Interface coupling is needed to ensure type safety. Your example is a bit too simple to properly see the merit in using composition over inheritance. The problems discussed here occur in deep inheritance hierarchies with many public and protected methods.

2

Your two examples both use the general concept of interface inheritance – which can be achieved in Java syntactically either by abstract classes with no protected members, or equivalently, by Java interfaces.

And when you combine “composition with interface inheritance”, it is no surprise you get the same amount of coupling in both cases.

If you want less coupling, use composition without a common Java interface – and voila, the necessity of changing the public method names of your composite when your “parts” change their public method names vanishes – which demonstrates less coupling.

3

You’ve got some good answers here, but I think a concrete example could help:

abstract class DatabaseObject {
    Connection getConnection() {
        // database stuff
    }

    abstract void load();

    abstract void save();
}

interface Person {
    String getName();
}

abstract class Employee implements Person extends DatabaseObject {
    private String name;

    String getName(){
        return name;
    }

    void load() {
        Connection connection = getConnection();
        // get name etc. from DB ...
    }

    void save() {
        Connection connection = getConnection();
        // write stuff to DB...
    }
}

abstract class Student implements Person extends DatabaseObject {
    private String name;

    String getName(){
        return name;
    }

    void load() {
        Connection connection = getConnection();
        // get name etc. from DB ...
    }

    void save() {
        Connection connection = getConnection();
        // write stuff to DB...
    }
}

You might notice that there’s a fair of code duplication here. That’s not exactly on topic but it does play into this.

I used to see this kind of thing a fair amount. It ‘works’ but creates a lot of challenges as a system evolves. For example, let’s say now that you have to support an alternate way of saving things to JSON files. What do we change?

We can introduce a new method called saveToFile or something and add that to the Employee and Student classes. But that doesn’t really help us becaause, Bob, the grumpy UI dev on the team is not going to add if statements in order to call your new method whenever the JSON save option is to be used. Your team insists that the JSON save functionality must happen in a save method. What are your options? If you want to stick with inheritance, you will need to introduce a new interface and a Json class. But you need to also add JsonStudent and JsonEmployee classes. The reason you have to do this is because your Student and Employee classes are coupled to the Database class through inheritance. And then guess what, you now get a requirement for yet another type of persistence. Do you see where this is going and why it’s a problem?

To avoid this kind of coupling, you can introduce composition:

interface Person {
    String getName();
    void save();
}

interface Persistence {
    Record load();
    void save(Record record);
}


abstract class Employee implements Person {
    private String name;

    Employee(Persistence) {
        // get name etc. Perstence ...
    }

    String getName(){
        return name;
    }

    void save();
}

abstract class Student implements Person {
    private String name;

    Employee(Persistence) {
        // get name etc. Perstence ...
    }

    String getName(){
        return name;
    }

    void save();
}

Now the Student and Employee types are decoupled from the database persistence. You can have any number of ways of persisting these objects without and changes to Student and Employee types. You might notice that this is also a way to do dependency injection (DI) and I would argue that composition is a prerequisite for an effective DI strategy.

This is obviously a trivially simple example, but this kind of issue crops up again and again when you attempt to inherit implementation details. instead of a nice modular system where you have a small number of pieces that you can put together in many different combinations, you end up with a combinatorial explosion of class definitions. One place where you can see how this tends to play out is in the Swing API.

2

I find these “inheritance isn’t so bad” posts fascinating, because “prefer composition” worked so well that new devs don’t even know how inheritance was used in the 90s. A typical example is:

class MySaveButton extends StdLibButton with ClickListenerInterface {
  def onClick(): Unit = ??? // Saving business logic
}

This pattern lets you put a MySaveButton anywhere a StdLibButton is expected. The implementation of onClick can directly use any StdLibButton methods like method() instead of button.method(). The code was often more concise than when using composition. So what’s the problem?

Your MySaveButton is in the StdLibButton inheritance hierarchy, which you have no control over, and it cannot inherit from any other class. Being tied so strongly to StdLibButton is usually fine at first, but limits your options down the road. You end up wanting to violate other design principles such as putting business logic in presentation layers, because you can’t create a separate business logic inheritance hierarchy. It’s also more difficult for the standard library developers, because they have to anticipate derived classes potentially overriding behavior in unexpected ways.

Modern development practice tends to do more inheritance from interfaces, which isn’t as problematic as classes because you can implement multiple interfaces. Additionally, modern languages have added features such as traits and multiple inheritance that make inheritance less problematic. Modern libraries tend to make their classes final so people can’t inherit from them even if they wanted to.

All those factors make the remaining uses of inheritance not so bad, but it took a while to get to that point.

LEAVE A COMMENT