handbookНаписать

lessons/001-refactor-to-strategy/step-2/README.md · тема · 4 языка · 39 тестов

Шаг 2: способы доставки за общим интерфейсом ​

Каждая ветка switch становится своим классом с методом cost, а калькулятор только выбирает, кому отдать посылку.

Урок: От switch к стратегии. Предыдущий шаг: одна функция на все способы. Следующий: реестр тарифов и правила из конфига.

Что было ​

Одна функция со switch по способу доставки и if по стране внутри каждой ветки. Цены, правила по странам и выбор способа жили в одном месте, и любая правка трогала всё сразу.

Что меняем ​

Делаем самый механический и самый безопасный ход: вырезаем каждую ветку в отдельный класс. Появляется контракт ShippingMethod с одним методом cost(Parcel), и три реализации: PickupPoint, Courier, Post. Код внутри них почти дословно тот, что был в ветках, только if по стране у курьера стал своим маленьким match.

Расчёт начатого килограмма переехал в Parcel::kilograms(): это знание о посылке, а не о способе доставки, и теперь его не нужно повторять.

ShippingCalculator::cost стал двумя строками: methodFor по коду способа возвращает нужный объект, а тот считает цену. Выбор всё ещё switch (в PHP match), и это сделано намеренно: за один шаг меняем одну вещь. Сначала разделили расчёт по классам, выбор трогаем на следующем шаге.

Тесты те же, что на шаге 1: те же шесть посылок с ценами и те же три отказа. В Go изменилась только строка вызова: функция ShippingCost стала методом Calculator.Cost. Тесты зелёные, значит поведение не поменялось.

Как это собирается ​

Сборка та же, что на шаге 1: калькулятор создаётся без аргументов, все способы доставки он создаёт сам внутри methodFor. Корзина спрашивает цену по каждому способу, express по-прежнему "not available".

Сценарий целиком с выводом: пример на PHP, он же на Go, Python и TypeScript в панели кода.

Что стало лучше ​

  • Каждый способ доставки читается и тестируется отдельно: (new Courier())->cost(...) не зависит от почты и пункта выдачи.
  • Правка тарифа курьера трогает только класс Courier. Остальные способы не открываются.
  • У стратегии есть имя. В разговоре "поправь курьера" теперь указывает на конкретный файл и класс, а не на строки 40-55.
  • Появился контракт ShippingMethod. Это шов, через который дальше пойдёт весь рефакторинг: кто угодно, кто умеет cost(Parcel), может стать способом доставки.

Что ещё болит ​

  • Добавить express - всё ещё правка methodFor. Калькулятор знает все способы поимённо и создаёт их сам, поэтому подменить способ в тесте или в другой стране нельзя.
  • Доступность по странам размазана по классам: пункт выдачи проверяет "только RU", курьер держит свой список стран. Ответ на вопрос "что вообще доступно в Беларуси" надо собирать по трём файлам.
  • Цены всё так же зашиты в код. Courier и Post при этом делают одно и то же: база плюс цена за килограмм, только с разными числами.
  • Каждый вызов cost создаёт новый объект способа. Здесь это дёшево, но сигнализирует о том же: объектами управляет не тот, кто должен.

Запуск: make test T=lessons/001-refactor-to-strategy/step-2

© 2014-2026 Тимур ТурдыевСайт и справочник работают на VPS от AdminVPS