Lesson 14 / 25

Composition Over Inheritance and Delegation

Build behaviour by combining objects and delegating instead of subclassing.

Assemble behaviour from parts

"Favour object composition over class inheritance" is one of the best-known design principles from the Design Patterns book (1994). Instead of inheriting behaviour, an object holds references to other objects and delegates work to them. Composition is flexible (behaviour can be swapped at run time by giving the object a different collaborator), less coupled (the object depends only on the collaborator's public interface, not on a parent's internals) and avoids class explosion: rather than EmailSmsNotifier, EmailPushNotifier and every other combination as subclasses, a Notifier holds a list of channels. Inheritance is still right for genuine is-a relationships with a stable, well-designed base class, such as framework extension points. A useful test: if you only want to reuse code, use composition; if you want substitutability (the subclass truly is a kind of the parent everywhere), inheritance may fit.

Replacing a subclass explosion with composition

Channels are separate objects; the notifier delegates to whichever it holds.

public interface Channel {
    void send(String to, String message);
}

public final class EmailChannel implements Channel {
    public void send(String to, String message) { /* SMTP */ }
}
public final class SmsChannel implements Channel {
    public void send(String to, String message) { /* SMS API */ }
}

public final class Notifier {
    private final List<Channel> channels;

    public Notifier(List<Channel> channels) { this.channels = List.copyOf(channels); }

    public void notify(String to, String message) {
        for (Channel c : channels) {
            c.send(to, message);                 // delegation
        }
    }
}

Notifier both = new Notifier(List.of(new EmailChannel(), new SmsChannel()));
// a new WhatsAppChannel needs no new Notifier subclass

A phone with apps versus many phones

Instead of buying a camera-phone, a music-phone and a map-phone, you have one phone and install apps. Composition lets an object gain abilities by plugging in parts, not by becoming a new kind of object for every combination.

Quick check: What is a main advantage of composition over inheritance?

  • It always runs faster
  • It removes the need for interfaces
  • It makes all fields public
  • Behaviour can be combined and changed by swapping collaborators, with less coupling to internals
Answer

Behaviour can be combined and changed by swapping collaborators, with less coupling to internals — Composition depends on interfaces of parts, which can be swapped and combined flexibly.