Зачем паттернам имена — Примеры
Пример 1 — Интерфейс с одной реализацией
Ревьюер видит форму ниже и спрашивает, не удалить ли её. Посчитайте реализации сегодня, скажите, что понадобится для второй, и чего будет стоить прямой вызов.
interface Notifier { send(user: User, message: string): Promise<void>; }
class EmailNotifier implements Notifier {
async send(user, message) { await this.mailer.send(user.email, message); }
}
class AlertService {
constructor(private notifier: Notifier) {}
async alert(user, message) { await this.notifier.send(user, message); }
}
Решение
реализаций сегодня: 1
если появится Slack-версия: добавить один класс и одну строку там, где
происходит выбор. Сам интерфейс не меняется.
вместо этого прямой вызов: AlertService принимает EmailNotifier и
вызывает send() на нём. Тогда добавление Slack означает правку
AlertService и каждого другого класса, который отправляет, вместе с их
тестами.
Интерфейс стоит один тип и один параметр конструктора; альтернатива обходится правкой у каждого вызывающего, как только появится второй случай, и приваривает логику оповещения к почтовику, который тесты не могут заменить. Именно этот компромисс сжимает имя — шов, чья ценность зависит от того, вероятна ли за ним вариация; а ловушка в том, чтобы отвечать правилом, «удалить, одна реализация» или «оставить, Slack может понадобиться», вместо подсчёта цены изменения каждого варианта.
Пример 2 — Условие, которому так и не дали имени
Метод ниже живёт в платёжном модуле. Скажите, какой паттерн он уже есть и что должно измениться, прежде чем его выделение станет оправданным.
async function pay(order) {
if (order.method === "card") {
const res = await stripe.charges.create(toStripe(order));
return { id: res.id, state: res.status };
}
if (order.method === "invoice") {
const inv = await ledger.raiseInvoice(toLedger(order));
return { id: inv.number, state: "open" };
}
throw new UnsupportedMethod(order.method);
}
Решение
это Стратегия без имени: один интерфейс, pay(order) -> { id, state },
выбор за вызывающим через order.method. Две ветви — две реализации, а
toStripe и toLedger — Адаптеры для чужой формы.
пока не стоит выделять: два случая в одном месте выбора. Выделяйте,
когда появится третий или когда второе место должно выбирать так же;
тогда выбор переезжает в Фабрику.
Назвать это стоит немедленно: ревью превращается из спора о ветвях в «это Стратегия с двумя реализациями», а два Адаптера уже показывают, почему чужие типы не должны протекать дальше. Выделение — отдельное решение, и его доказательство — третий случай или второе место выбора, а не опрятность. Ловушка — выделять на двух случаях и купить Фабрику, интерфейс и два класса вместо шести читаемых строк.