Unit 10 · lesson
An Interface Describes What a Collaborator Can Do
Imagine a program that needs to send a status message. The program should not need to know whether the destination is a console, memory buffer, classroom display, or another approved output mechanism.
Define the capability:
interface MessageSink {
void send(String message);
}
An interface separates caller assumptions from implementation details
The caller depends on a capability contract rather than one concrete class.
- CALLERneeds a capabilitydepend on
- INTERFACEdeclares operations available to the callerimplemented by
- IMPLEMENTATION Aone concrete behavior behind the contractor
- IMPLEMENTATION Bdifferent internals satisfy the same capabilityverify
- BEHAVIORAL CONTRACTtests confirm promised behavior
The interface does not say how the message is stored or displayed. It says what a compliant collaborator must be able to do.
Implement the capability
class ConsoleSink implements MessageSink {
public void send(String message) {
IO.println(message);
}
}
Another implementation could collect messages:
class MemorySink implements MessageSink {
private final List<String> messages = new ArrayList<>();
public void send(String message) {
messages.add(message);
}
int count() {
return messages.size();
}
}
Both types can be viewed through the MessageSink contract.
Capability is not ancestry
The important statement is:
ConsoleSink can act as a MessageSink
MemorySink can act as a MessageSink
The two classes do not need to share a parent class containing message logic.
That separation is powerful because common behavior can be expressed without forcing unrelated implementation state into one inheritance tree.
Method signatures are only part of the contract
This technically implements the method:
class BrokenSink implements MessageSink {
public void send(String message) {
// do nothing
}
}
The compiler sees a matching method signature. But whether this satisfies the intended behavior depends on the actual contract.
If send promises that the supplied message becomes observable at the destination, silently dropping it violates the behavioral contract even though compilation succeeds.
Interfaces create structural obligations. Designers and tests still have to define behavioral expectations.
Model a capability
Choose one capability:
Scorablewithint score();Validatablewithboolean isValid();FormatterwithString format();Commandwithvoid execute();ReadingSourcewithdouble read().
Create two implementations that produce meaningfully different behavior while satisfying the same interface.
Then write one method that accepts the interface type and works with either implementation.
Evidence
State what the caller is allowed to assume and what implementation details it should not need to know.
If the caller still checks if (thing instanceof ConcreteA) for ordinary behavior, investigate whether your interface really captured the needed capability.