Зачем паттернам имена — Примеры

Пример 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 — Адаптеры для чужой формы.

пока не стоит выделять: два случая в одном месте выбора. Выделяйте,
когда появится третий или когда второе место должно выбирать так же;
тогда выбор переезжает в Фабрику.

Назвать это стоит немедленно: ревью превращается из спора о ветвях в «это Стратегия с двумя реализациями», а два Адаптера уже показывают, почему чужие типы не должны протекать дальше. Выделение — отдельное решение, и его доказательство — третий случай или второе место выбора, а не опрятность. Ловушка — выделять на двух случаях и купить Фабрику, интерфейс и два класса вместо шести читаемых строк.