Как доработать код?

Здравствуйте, у меня есть класс сервиса, который занимается редактированием записей. Основной метод выглядит так
public function edit(): bool
{
    /** @var bool $result Переменная для записи результата. */
    $result = false;

    switch ($this->type) {
        case 'hide':
            $result = $this->hideSubject();
            break;

        case 'fast-edit':
            $result = $this->fastEdit();
            break;
    }

    return $result;
}

Подскажите, как можно доработать этот метод? Если придется еще один вид редактирования добавить, придется добавлять еще один блок case, что не соответствует принципу открытости закрытости как минимум. Заранее спасибо!
  • Вопрос задан
  • 378 просмотров
Пригласить эксперта
Ответы на вопрос 4
@Vitsliputsli
Ничего страшного, допишите еще один case и метод, и все будет нормально. Да, это нарушает принцип, но все так делают, просто хрен кто признается. Не пытайтесь здесь вкрячить какого-нибудь монстра, сложный код - это потенциальные ошибки, если возможно написать просто, так и нужно сделать.
Принципы - они принципы, а не законы, надо следовать не букве, а смыслу. Принцип открытости и закрытости оберегает нас от ошибок и дополнительных затрат на тестирование кода, который вроде бы и так уже работает и хорошо себя зарекомендовал. Т.е. если бы у вас был один метод и вы там внутри как-то хитро разруливали работу с разными типами и при добавлении нового типа изменяли бы его - это было бы ужасно. В текущей реализации, вам нужно будет добавить новый метод (это не изменит поведение класса до вмешательства), и добавить новый путь при использовании нового типа - да, вмешательство, но оно минимально. Если умудритесь накосячить здесь, то вас уже никакие принципы не спасут.
Если же у нас, что-то гораздо более сложное, либо класс физически недоступен для изменений, или он уже вовсю используется, а новый тип нужен только для конкретной реализации, то, пожалуйста, есть наследование. Наследуете класс, в потомке добавляете метод и заменяете метод, выполняющий перенаправление (не забывайте, что есть вызов parent). Это будет полностью соответствовать принципу.
Но я бы больше уделил внимание тому, почему мы ориентируемся для выбора метода на внутреннее свойство, точно ли это должны быть методы, а не отдельные классы. И вполне может быть получится так, что все эти танцы с бубнами не нужны.
Ответ написан
Комментировать
@oxidmod
Нужно внедрить стратегии, выбирать стратегию по типу и вызывать у нее метод
interface EditStrategyInterface {
    public function edit(): bool;
}

class HideStrategy implements EditStrategyInterface {
    public function edit(): bool {
         echo 'hide';
    }
}

class FastEditStrategy implements EditStrategyInterface {
    public function edit(): bool {
         echo 'fast edit';
    }
}

// your service class
public function edit(): bool
{
    $strategy = $this->strategies[$this->type] ?? null;

    if (!$strategy) return false;

    return $strategy->edit();
}


Конечно над самим интерфейсом EditStrategyInterface нужно подумать, что туда нужно передать, чтоб обеспечить работу стратегии (подозреваю, что передать надо какую-то модельку)
Ответ написан
Adamos
@Adamos
public function edit(): bool
{
    $possible = [
        'hide' => 'hideSubject',
        'fast-edit' => 'fastEdit'
    ];
    if(array_key_exists($this->type, $possible)) {
        return $this->$possible[$this->type]();
    }
    return false;
}

Но при всей лаконичности этого решения у него есть очень серьезный недостаток.
Совершив опечатку, вы узнаете о ней только тогда, когда выпадет конкретный кейс - никакие инструменты ее не помогут заметить.
И поиск в IDE по коду, где вызываются конкретные методы, это место - не покажет.
Так что использование его внутри одного класса еще может быть оправдано. А вот как диспетчер публичных методов - it smells a lot.
Ответ написан
Комментировать
@artalexs Автор вопроса
Почитав ответы, решил немного переделать структуру сервиса (пишу на Yii2) и сделать через DI контейнер. Структура такая - EditServiceInterface, который будет имплементирован в классы, (FastEdit и HideSubject), в которых будут реализованы соответсвующие методы; в конфиге:
'container' => [
    'singletons' => [
        'fast-edit' => function () {
            return new \frontend\services\editing\FastEdit();
        },
        'hide' => function () {
            return new \frontend\services\editing\HideSubject();
        }
    ],
],


И в нужном месте вызываю Yii::$container->get($type)
Думаю такой вариант будет оптимален и без лишнего усложнения.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
ManyChat Москва
от 150 000 ₽
Depcon Екатеринбург
от 100 000 до 170 000 ₽
от 210 000 до 250 000 ₽