Рефакторинги, которыми вы действительно воспользуетесь — Примеры

Пример 1 — Цепочка условий по одному значению

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

function multiplier(channel) {
  if (channel === "retail") return 1.2;
  if (channel === "wholesale") return 1.07;
  if (channel === "partner") return 1.1;
  throw new Error("unknown channel: " + channel);
}

Решение

const MULTIPLIERS = { retail: 1.2, wholesale: 1.07, partner: 1.1 };

function multiplier(channel) {
  const rate = MULTIPLIERS[channel];
  if (rate === undefined) throw new Error("unknown channel: " + channel);
  return rate;
}

// добавление "marketplace" — одна строка в одном месте

Таблица даёт вариации имя и делает каждый будущий канал однострочным добавлением. Важна здесь явная проверка на undefined: наивное MULTIPLIERS[channel] ?? 1 молча оценило бы неизвестный канал по себестоимости, тогда как цепочка прежде отказывала. Названные ходы безопасны только тогда, когда сохраняют поведение, которое вы не меняете, — здесь возбуждение ошибки и есть поведение, и его удаление превращает уборку в дефект.

Пример 2 — Два аргумента, которые всегда путешествуют вместе

Функции нужны хост и порт, и вызывающие постоянно передают их в неверном порядке. Дайте паре имя, одно место для валидации и сигнатуру, которую невозможно прочитать неправильно.

function connect(host: string, port: number, retries: number) {
  // вызывающие должны помнить порядок, и значения никто не проверяет
}

connect("db.internal", 5432, 3);
connect("db.internal", 3, 5432);   // компилируется и набирает порт 3

Решение

class Endpoint {
  constructor(readonly host: string, readonly port: number) {
    if (!Number.isInteger(port) || port < 1 || port > 65535) {
      throw new Error("invalid port: " + port);
    }
  }
}

function connect(to: Endpoint, retries: number) { /* ... */ }

connect(new Endpoint("db.internal", 5432), 3);

Теперь у двух значений один тип, поэтому неверный порядок больше не выразим, а проверка диапазона имеет единственный дом вместо копии в каждом месте вызова. Объект-значение даёт паре имя, общее для компилятора и читателя, — та же выгода, что и от хорошего имени функции, только в меньшем масштабе; вводить его стоит, когда значения действительно принадлежат друг другу, а не просто потому, что два аргумента стоят рядом.