Рефакторинг switch на PHP

Оператор switch или множественные if зачастую являются признаком некачественного кода. Попробуем сделать рефакторинг для абстрактной задачи. Пусть некая функция будет нам возвращать Hex значение цвета, далее пример плохого кода:


class Refactoring
{
    public function getColorWithIf(string $color): ?string
    {
        if ($color === 'red') {
            return '#FF0000';
        }
        if ($color === 'blue') {
            return '#0000FF';
        }
        return null;
    }

    public function getColorWithSwitch(string $color): ?string
    {
        switch ($color) {
            case 'red':
                return '#FF0000';
            case 'blue':
                return '#0000FF';
        }
        return null;
    }
}

$test = new Refactoring();
var_dump($test->getColorWithIf('red')); // string(7) "#FF0000"
var_dump($test->getColorWithSwitch('red')); // string(7) "#FF0000"

Начнем рефакторинг с простого, но весьма эффективного метода - использование хеш-таблиц:


class Refactoring
{
    public function getColorWithArray(string $color): ?string
    {
        $colors = [
            'red' => '#FF0000',
            'blue' => '#0000FF',
        ];
        return $colors[$color] ?? null;
    }
}

$test = new Refactoring();
var_dump($test->getColorWithArray('red')); // string(7) "#FF0000"

Метод очень наглядный, легко правится, масштабируется и в то же время быстро работает. Можно смело рекомендовать. С приходом PHP 8.0 то же самое можно сделать с помощью функции match:


class Refactoring
{
    public function getColorWithMatch(string $color): ?string
    {
        return match ($color) {
            'red' => '#FF0000',
            'blue' => '#0000FF',
            default => null,
        };
    }
}

$test = new Refactoring();
var_dump($test->getColorWithMatch('red')); // string(7) "#FF0000"

PHP 8.1 подарил нам перечисления. В некоторых случаях их тоже можно использовать для рефакторинга, например:


enum Colors: string
{
    case red = '#FF0000';
    case blue = '#0000FF';

    public static function valueFromName(string $name): self
    {
        return constant("self::$name");
    }
}

class Refactoring
{
    public function getColorWithEnum(string $color): ?string
    {
        // PHP 8.3
        // return Colors::{$color}->value;
        return Colors::valueFromName($color)->value;
    }
}

$test = new Refactoring();
var_dump($test->getColorWithEnum('red')); // string(7) "#FF0000"

Для PHP 8.1 и 8.2 в перечисление приходится добавлять дополнительную функцию valueFromName, которая вытаскивает значение по ключу. В PHP 8.3 появился динамический доступ к константам и можно просто использовать выражение Colors::{$color}. У такого подхода появляются дополнительные преимущества в виде безболезненного повторного использования и вынесения логики выбора в отдельную сущность.

Попробуем сделать рефакторинг используя полиморфизм и паттерн "Состояние":


interface ColorInterface
{
    public function getColor(): string;
}

class Red implements ColorInterface
{
    public function getColor(): string
    {
        return '#FF0000';
    }
}

class Blue implements ColorInterface
{
    public function getColor(): string
    {
        return '#0000FF';
    }
}

class ColorState
{
    private ColorInterface $state;

    public function __construct(string $color)
    {
        $this->transitionTo($color);
    }

    public function transitionTo(string $color): void
    {
        $this->state = new $color;
    }

    public function getColor(): string
    {
        return $this->state->getColor();
    }
}

class Refactoring
{
    public function getColorWithState(string $color): ?string
    {
        return (new ColorState($color))->getColor();
    }
}

$test = new Refactoring();
var_dump($test->getColorWithState('red')); // string(7) "#FF0000"

Не буду подробно рассказывать о достоинствах этого способа, скажу лишь, что за ним стоит вся мощь ООП. Но обязательно должен отметить неочевидный, но существенный недостаток - встреча подобного кода ставит в тупик начинающих разработчиков. Будьте аккуратны, если в вашей команде много джунов.

29.11.2024