Что такое интерфейс компонента

Интерфейс компонента — это описание всех операций, предоставляемых компонентом. Интерфейс компонента — это множество операций, доступных через фасад компонента. Фасад компонента — это корень интерфейса.

Концептуально компонент можно выразить формулой:

Component exposes Interface
Component.start() → Facade
Facade provides access to Interface

Выражение Component exposes Interface означает, что компонент предоставляет описание доступных операций, например, в виде набора классов или HTTP-точек. Component.start() → Facade говорит, что фасад становится доступен только после вызова точки запуска, а Facade provides access to Interface — что именно через фасад доступны все объявленные операции.

Интерфейс компонента может быть описан разными языками. В модульном монолите он выражается сигнатурами Java-методов, а в распределённой системе — описаниями HTTP-эндпоинтов, определениями RPC-вызовов и т. п.

Границы интерфейса компонента определяются тем, какие операции доступны клиенту через фасад. На языке Java это будут все поля и методы, до которых можно добраться от фасада по цепочкам вызовов, а также конструкторы тех классов, объекты которых эти методы принимают в качестве аргументов. Те поля и методы, которые в этих цепочках не участвуют, в интерфейс не входят. Поясним это на примере программы карточной игры «21»[*]:

Предположим, взаимодействие компонентов, клиента и сервера, будет организовано через RMI-протокол.

public interface Table extends Remote {
    Player sitDown() throws RemoteException;
}
public interface Player extends Remote {
    int drawNextCard() throws RemoteException;
    void passTurn() throws RemoteException;
}
public class RegularTable extends UnicastRemoteObject implements Table {
    private final List<RegularPlayer> players = new ArrayList<>();
    private final Deck deck = new Deck();
    private int turn = 0;

    public RegularTable() throws RemoteException {}

    public Player sitDown() throws RemoteException {
        var player = new RegularPlayer(this, deck);
        players.add(player);
        return player;
    }

    boolean isTurnOf(RegularPlayer p) {
        return turn < players.size() && players.get(turn) == p;
    }

    void passTurn(RegularPlayer p) {
        if (isTurnOf(p)) turn++;
    }
}
public class RegularPlayer extends UnicastRemoteObject implements Player {
    private final RegularTable table;
    private final Deck deck;
    private int sum = 0;

    public RegularPlayer(RegularTable table, Deck deck) throws RemoteException {
        this.table = table;
        this.deck = deck;
    }

    public int drawNextCard() {
        if (!table.isTurnOf(this)) {
            throw new IllegalStateException("Not your turn.");
        }
        return sum += deck.draw().rank();
    }

    public void passTurn() {
        table.passTurn(this);
    }
}
public class Deck {
    public Card draw() { ... }
}
public class Card {
    public int rank() { ... }
}

Java-интерфейс Table является одновременно и фасадом, поскольку именно через него происходит вход в интерфейс компонента, и составляющей частью интерфейса компонента, поскольку объявляет метод sitDown. Возвращаемый методом sitDown объект типа Player открывает доступ к drawNextCard и passTurn, поэтому эти методы также входят в интерфейс компонента. Метод Deck.draw, хотя и объявлен как публичный, в интерфейс компонента не входит, поскольку он не доступен через фасад.


[*] «21»: каждый игрок одну за другой берёт карты из колоды и складывает их номиналы. Можно остановиться в любой момент. Выигрывает игрок, набравший наибольшую сумму, не превысившую 21.