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);
}
Concept flow

An interface separates caller assumptions from implementation details

The caller depends on a capability contract rather than one concrete class.

  1. CALLERneeds a capability
    depend on
  2. INTERFACEdeclares operations available to the caller
    implemented by
  3. IMPLEMENTATION Aone concrete behavior behind the contract
    or
  4. IMPLEMENTATION Bdifferent internals satisfy the same capability
    verify
  5. 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:

  • Scorable with int score();
  • Validatable with boolean isValid();
  • Formatter with String format();
  • Command with void execute();
  • ReadingSource with double 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.