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