Последовательная связанность.
Последовательная связанность — антипаттерн, при котором класс работает правильно, только если его методы вызываются в строго определённом порядке.
В объектно-ориентированном программировании последовательная связанность (также известная как временная связанность) — это форма связанности, при которой класс требует, чтобы его методы вызывались в определённой последовательности. В зависимости от контекста это может быть антипаттерном.
Методы, имена которых начинаются с 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; текст переведён и отредактирован, добавлены примеры.