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));
}
}
Composition assigns responsibilities to collaborators
Has-a relationships let a coordinator delegate focused work without a false is-a claim.
- COORDINATORowns orchestrationhas
- COLLABORATOR Aowns one coherent responsibilityand
- COLLABORATOR Bowns another coherent responsibilitydelegate
- BOUNDARIEScomponents can change behind contractsproduce
- 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.