Шаг 1: одна функция на все способы доставки
Исходный код:
switchпо способу доставки, внутриifпо стране, цены зашиты прямо в ветках.
Урок: От switch к стратегии. Следующий шаг: способы доставки за общим интерфейсом.
Что было
Магазин считает доставку на кассе. Покупатель выбирает способ (pickup, courier, post), корзина знает страну, вес и сумму заказа. Всё это попадает в Parcel, а цену возвращает одна функция: ShippingCalculator::cost в PHP, ShippingCost в Go, shipping_cost в Python и shippingCost в TypeScript.
Правила такие:
- пункт выдачи только по России, 199 рублей, бесплатно от 3000;
- курьер по России 390 плюс 50 за каждый начатый килограмм, по Беларуси 590 плюс 80;
- почта по России 290 за любую посылку, за границу 1490 плюс 150 за килограмм;
- всё остальное - ошибка
UnsupportedShippingс текстом "courier is not available for KZ".
Деньги в копейках, целыми числами. Вес округляется вверх до начатого килограмма.
Что меняем
Пока ничего. Это точка отсчёта: код работает, тесты зелёные, и именно эти тесты дальше будут держать рефакторинг. Перед тем как двигать код, нужно зафиксировать поведение, иначе непонятно, что сломалось - устройство или результат.
Тесты описывают правила, а не устройство функции. Таблица из шести посылок проверяет цены: пункт выдачи, бесплатный пункт выдачи от порога, курьер по России и Беларуси, почта по России и за границу. Вторая таблица проверяет отказы: пункта выдачи нет в Беларуси, курьера нет в Казахстане, способа drone нет вообще. На следующих шагах эти таблицы не меняются, меняется только строка, которая создаёт калькулятор.
Как это собирается
Собирать нечего: калькулятор не зависит ни от чего и создаётся одной строкой. Корзина просит цену по каждому способу и показывает недоступные как "not available". В списке есть express: маркетинг уже попросил экспресс-доставку по России за 990, но в этом коде её пока нет.
Сценарий целиком с выводом: пример на PHP, он же на Go, Python и TypeScript в панели кода.
Что стало лучше
Ничего, но кое-что здесь честно хорошо: весь расчёт виден на одном экране, и для трёх способов в двух странах этого может хватать годами. Если новые способы не появляются, на этом шаге можно остановиться.
Что ещё болит
- Добавить
express- значит открыть функцию, которую трогают все, и вставить ещё одну ветку. Тесты на старые способы приходится гонять заново, потому что правка шла в том же месте. - Цены, правила по странам и выбор способа перемешаны. Нельзя проверить курьера отдельно от почты: тест всегда идёт через весь
switch. - Тарифы зашиты в код. Поменять 390 на 420 - это релиз, хотя логика не менялась.
- Расчёт килограммов делается для всех способов, хотя нужен двум из трёх. Общий код в начале функции - первый признак того, что ветки хотят разойтись.
Запуск: make test T=lessons/001-refactor-to-strategy/step-1