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