Unit 11 · lesson

Has-a Is Often More Useful Than Is-a

Suppose a notification service needs two capabilities:

format a message
send a message

One design is composition:

class NotificationService {
    private final Formatter formatter;
    private final MessageSink sink;

    NotificationService(Formatter formatter, MessageSink sink) {
        this.formatter = formatter;
        this.sink = sink;
    }

    void notify(String label, int value) {
        sink.send(formatter.format(label, value));
    }
}
Concept flow

Composition assigns responsibilities to collaborators

Has-a relationships let a coordinator delegate focused work without a false is-a claim.

  1. COORDINATORowns orchestration
    has
  2. COLLABORATOR Aowns one coherent responsibility
    and
  3. COLLABORATOR Bowns another coherent responsibility
    delegate
  4. BOUNDARIEScomponents can change behind contracts
    produce
  5. SYSTEM BEHAVIORcomposed parts cooperate

Read the relationships:

NotificationService has a Formatter
NotificationService has a MessageSink

It does not need to be either collaborator.

Composition delegates responsibility

The service coordinates behavior. It does not need to know formatting internals or output internals.

That gives each component a smaller reason to change:

  • formatting rule changes -> formatter;
  • destination changes -> sink;
  • orchestration changes -> service.

This separation is not free. More components mean more boundaries to name and test. Use composition when those responsibilities are meaningfully different.

Compare with a giant class

A giant NotificationManager might contain:

  • formatting rules;
  • message history;
  • console output;
  • file output;
  • validation;
  • retry logic;
  • user preferences.

It may run, but every change touches a class that knows too much.

Draw the responsibilities and ask which ones can have independent contracts.

Domain composition

A robot model might be:

class Robot {
    private final DriveSystem drive;
    private final SensorSource sensor;
}

The robot has a drive system. Making Robot extends DriveSystem would claim that every Robot is a DriveSystem, which misrepresents the domain.

Inheritance is not a general-purpose glue keyword.

Build a composed system

Choose one:

  • report generator with formatter + output sink;
  • scoreboard with scoring policy + display;
  • study planner with scheduling policy + notifier;
  • robot controller with reading source + decision policy.

Give each collaborator one coherent responsibility.

Implement the coordinator so it receives collaborators rather than constructing every concrete dependency internally.

Evidence

Submit a component diagram with has-a labels. For each object, write one sentence beginning:

This component owns the responsibility for...

Then name one responsibility you intentionally kept out of the coordinator.

If every component sentence sounds identical, the decomposition is probably artificial.