Логотип ConstructiCat
CodeBust.
Навигация по разделу ▾

Последовательная связанность.

Последовательная связанность — антипаттерн, при котором класс работает правильно, только если его методы вызываются в строго определённом порядке.

В объектно-ориентированном программировании последовательная связанность (также известная как временная связанность) — это форма связанности, при которой класс требует, чтобы его методы вызывались в определённой последовательности. В зависимости от контекста это может быть антипаттерном.

Методы, имена которых начинаются с Init, Begin, Start и т. п., могут указывать на наличие последовательной связанности.

Если воспользоваться аналогией с автомобилем: когда пользователь жмёт на газ, не запустив сначала двигатель, машина не ломается, не отказывает и не выбрасывает исключение — она просто не разгоняется.

Последовательную связанность можно отрефакторить с помощью шаблонного метода, чтобы преодолеть проблемы, возникающие из-за использования этого антипаттерна.

Пример

Этот Parser работает, только если его методы вызываются строго в нужном порядке. Интерфейс класса об этом никак не сообщает: стоит забыть вызвать tokenize(), и buildTree() упадёт во время выполнения с NullPointerException.

class Client {
    Node run(String text) {
        Parser parser = new Parser();
        parser.setInput(text); // должен вызываться первым
        parser.tokenize();     // должен вызываться вторым
        return parser.buildTree();
    }
}

class Parser {
    private String input;
    private List<String> tokens;

    void setInput(String text) { this.input = text; }
    void tokenize() { tokens = List.of(input.trim().split("\\s+")); }
    Node buildTree() { return new Node(List.copyOf(tokens)); } // NullPointerException, если пропустить tokenize()
}

Теперь единственный публичный метод сам выполняет шаги и передаёт каждому результат предыдущего, поэтому неправильный порядок вызовов просто невозможно записать:

class Client {
    Node run(String text) {
        return new Parser().parse(text);
    }
}

final class Parser {
    Node parse(String text) {
        List<String> tokens = tokenize(text);
        return buildTree(tokens);
    }

    private List<String> tokenize(String text) { return List.of(text.trim().split("\\s+")); }
    private Node buildTree(List<String> tokens) { return new Node(List.copyOf(tokens)); }
}

Если подклассам нужно переопределять отдельные шаги, ту же гарантию даёт паттерн Шаблонный метод: базовый класс фиксирует порядок, а подклассы переопределяют только сами шаги.

Адаптировано по статье Википедии “Sequential coupling” (версия 1320717439), распространяется по лицензии CC BY-SA 4.0; текст переведён и отредактирован, добавлены примеры.