Skip to content

Instantly share code, notes, and snippets.

@v0ff4k
Created June 2, 2026 19:50
Show Gist options
  • Select an option

  • Save v0ff4k/1e4d3fd4adb1c41a4ac106c175a3f5bc to your computer and use it in GitHub Desktop.

Select an option

Save v0ff4k/1e4d3fd4adb1c41a4ac106c175a3f5bc to your computer and use it in GitHub Desktop.
refactoring DeliveryCalculator

Задание

Есть исходный(старый) класс, который использует IF-ELSE простыню(ад)

/**
 * Class DeliveryCalculatorOld
 * old style
 */
class DeliveryCalculatorOld
{
    public function cost(Order $order, string $type): float
    {

        if ($type === 'courier') {
            return 300 + $order->weight * 10;
        } elseif ($type === 'pickup') {
            return 0;
        } elseif ($type === 'post') {
            return 150 + $order->weight * 5;
        }

        throw new \InvalidArgumentException("Unknown type: $type");
    }
}

Необходимо улучшить: "добавить: express, drone" + "заложить расширяемость"

Проведу рефакторинг + заложить новый стиль с OOP + DRY + ACID + SOLID

Заготовка из исходников

/**
 * Class Order
 * Zero boiler, safe, immutable
 */
class Order
{
    public function __construct(
        public readonly float $weight,
        // You can add other typical order data here later:
        // public readonly int $id,
        // public readonly string $status,
    ) {}
}

interface DeliveryCostCalculatorInterface
{
    public function calculate(Order $order): float;
}

Типы доставки

class CourierDeliveryCalculator implements DeliveryCostCalculatorInterface
{
    public function calculate(Order $order): float
    {
        return (float)(300 + $order->weight * 10);
    }
}

class PickupDeliveryCalculator implements DeliveryCostCalculatorInterface
{
    public function calculate(Order $order): float
    {
        return (float)(0);
    }
}

class PostDeliveryCalculator implements DeliveryCostCalculatorInterface
{
    public function calculate(Order $order): float
    {
        return (float)(150 + $order->weight * 5);
    }
}

// 2+ express, drone

/**
 * Class ExpressDeliveryCalculator
 */
class ExpressDeliveryCalculator implements DeliveryCostCalculatorInterface
{
    public function calculate(Order $order): float
    {
        return (float)(123 + $order->weight * 50);
    }
}

/**
 * Class DroneDeliveryCalculator
 */
class DroneDeliveryCalculator implements DeliveryCostCalculatorInterface
{
    public function calculate(Order $order): float
    {
        return (float)(10 + $order->weight * 2);
    }
}

Factory для склейки в авторежиме

/**
 * Class DeliveryCalculatorFactory
 */
class DeliveryCalculatorFactory
{
    /**
     * @todo update processing to CamelCase from snake_case | kebab-case (not only flat)
     * @param string $type
     * @return \DeliveryCostCalculatorInterface
     */
    public function __invoke(string $type): DeliveryCostCalculatorInterface
    {
        $className = ucfirst(strtolower($type)) . 'DeliveryCalculator';
        // $className = "App\\Services\\" . ucfirst(strtolower($type)) . 'DeliveryCalculator';
        if (!class_exists($className)) {
            throw new \InvalidArgumentException("Unknown type: $type (Class $className not found)");
        }
        if (!is_subclass_of($className, DeliveryCostCalculatorInterface::class)) {
            throw new \UnexpectedValueException("Class $className must implement DeliveryCostCalculatorInterface");
        }

        return new $className();
    }
}

Где-то в клиентском коде(перед вызовом можно добавить перебор/проверку $type)

/**
 * Class DeliveryCalculator
 */
class DeliveryCalculator
{
    /**
     * Dynamically loads the specific class and runs calculate()
     * @param \Order $order
     * @param string $type
     * @return float
     */
    public function cost(Order $order, string $type): float
    {
        $factory = new DeliveryCalculatorFactory();

        return ($factory)($type)->calculate($order);
    }
}

// run(test):
echo "\n".(new DeliveryCalculator())->cost(new Order(21.5), 'drone'); // 53
echo "\n".(new DeliveryCalculator())->cost(new Order(1.5), 'express'); // 198
echo "\n".(new DeliveryCalculator())->cost(new Order(82.5), 'post'); // 562.5
echo "\n".(new DeliveryCalculator())->cost(new Order(23.5), 'pickup'); // 0
echo "\n".(new DeliveryCalculator())->cost(new Order(0.15), 'courier'); // 301.5
// Uncaught InvalidArgumentException: Unknown type: nonexist (Class NonexistDeliveryCalculator not found)
//echo "\n".(new DeliveryCalculator())->cost(new Order(6.65), 'nonexist'); // 301.5
echo "\n --=:END:=-- \n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment