Работа с вложенными операторами if if else / nested switch - PullRequest
11 голосов
/ 30 января 2009

Существуют ли какие-либо шаблоны / методы / способы удаления вложенных условий условия / переключателя if not else?

Я помню, что сталкивался с некоторыми методами, используемыми людьми Google, перечисленными в сообщении в блоге кода Google. Кажется, не могу найти его сейчас, хотя

Ответы [ 7 ]

8 голосов
/ 30 января 2009

Читали ли вы это об уплощении кода стрелки из Coding Horror?

Вы можете заменить throw на return или goto, если вы используете язык без исключений.

6 голосов
/ 30 января 2009

Вы хотите использовать рефакторинг, который заменяет условный, используя полиморфный класс. Для пример .

Или вот еще один пример

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

Редактировать

Позвольте мне добавить, что это не идеальное решение в каждом случае. Как (я забыл ваше имя, извините) указал в моих комментариях, иногда это может быть болезненно, особенно если вам нужно создать объектную модель просто для этого. Этот рефакторинг превосходит, если у вас есть это:

function doWork(object x)
{

   if (x is a type of Apple)
   {
      x.Eat();

   } else if (x is a type of Orange)
   {
      x.Peel();
      x.Eat();
   }

}

Здесь вы можете изменить рефакторинг на новый метод, который будет обрабатывать каждый фрукт.

Редактировать

Как кто-то указал, как создать правильный тип для перехода в doWork, есть больше способов решить эту проблему, чем я мог бы перечислить несколько основных. Первое и самое прямое (и да идет против сути этого вопроса) это переключатель:

class FruitFactory
{
   Fruit GetMeMoreFruit(typeOfFruit)
   {
         switch (typeOfFruit)
         ...
         ...
   }
}

Хорошая особенность этого подхода в том, что его легко написать, и обычно это первый метод, который я использую. Хотя у вас все еще есть оператор switch, он изолирован для одной области кода и является очень простым, но все, что он возвращает, - это n-объект. Если у вас есть только несколько объектов, и они не меняются, это работает очень хорошо.

Другие более сложные шаблоны, которые вы можете посмотреть, - это Abstract Factory . Вы также можете динамически создавать Fruit, если ваша платформа поддерживает его. Вы также можете использовать что-то вроде Provider Pattern . По сути, для меня это означает, что вы конфигурируете свой объект, и тогда у вас есть фабрика, которая на основе конфигурации и ключа, который вы даете фабрике, динамически создает правильный класс.

3 голосов
/ 30 января 2009

Я действительно написал о том, как решить эту проблему, в своем блоге в апреле 2008 года. Посмотрите здесь и дайте мне знать, что вы думаете.

Я рекомендую вам:

  1. Используйте полиморфизм для получения нужного поведения во время выполнения без условных операторов.

  2. Возьмите все свои условные операторы и переместите их в своего рода "фабрику", которая даст вам соответствующий тип во время выполнения.

  3. Вы сделали. Разве это не было легко? :)

Если вы хотите увидеть пример реального кода о том, как преобразовать ваш код, зайдите в мой блог.

P.S. Это не дешевая попытка саморекламы; Я долгое время являлся SO-пользователем, и это первый раз, когда я связывался со своим блогом - и я сделал это только потому, что считаю его актуальным.

2 голосов
/ 30 января 2009

Вы думали о ролике Google " The Clean Code Talks - Наследование, полиморфизм и тестирование "? В нем обсуждаются подходы к использованию объектно-ориентированных методов для удаления условных выражений if / switch.

1 голос
/ 30 января 2009

Возможно, вы захотите взглянуть на шаблон стратегии , в котором вместо помещения длинной цепочки if со связанными условиями вы абстрагируете каждое условие в отдельный объект, каждый из которых определяет его специфическое поведение.

Класс, который определяет эти объекты, будет реализовывать интерфейс, который будет вызываться родительским объектом.

1 голос
/ 30 января 2009

Вы не говорите, какой язык вы используете, но если вы используете язык OO, такой как C ++, C # или Java, вы часто можете использовать виртуальные функции , чтобы решить ту же проблему, что и вы в настоящее время решаются с помощью оператора switch и более расширяемым способом. В случае C ++ сравните:

class X {
public:
    int get_type();     /* Or an enum return type or similar */
    ...
};

void eat(X& x) {
    switch (x.get_type()) {
    TYPE_A: eat_A(x); break;
    TYPE_B: eat_B(x); break;
    TYPE_C: eat_C(x); break;
    }
}

void drink(X& x) {
    switch (x.get_type()) {
    TYPE_A: drink_A(x); break;
    TYPE_B: drink_B(x); break;
    TYPE_C: drink_C(x); break;
    }
}

void be_merry(X& x) {
    switch (x.get_type()) {
    TYPE_A: be_merry_A(x); break;
    TYPE_B: be_merry_B(x); break;
    TYPE_C: be_merry_C(x); break;
    }
}

с

class Base {
    virtual void eat() = 0;
    virtual void drink() = 0;
    virtual void be_merry() = 0;
    ...
};

class A : public Base {
public:
    virtual void eat() { /* Eat A-specific stuff */ }
    virtual void drink() { /* Drink A-specific stuff */ }
    virtual void be_merry() { /* Be merry in an A-specific way */ }
};

class B : public Base {
public:
    virtual void eat() { /* Eat B-specific stuff */ }
    virtual void drink() { /* Drink B-specific stuff */ }
    virtual void be_merry() { /* Be merry in an B-specific way */ }
};

class C : public Base {
public:
    virtual void eat() { /* Eat C-specific stuff */ }
    virtual void drink() { /* Drink C-specific stuff */ }
    virtual void be_merry() { /* Be merry in a C-specific way */ }
};

Преимущество состоит в том, что вы можете добавлять новые Base -производные классы D, E, F и т. Д. Без необходимости касаться кода, который имеет дело только с указателями или ссылками на Base, поэтому ничто не может устареть так, как это может сделать оператор switch в исходном решении. (Преобразование выглядит очень похоже в Java, где методы по умолчанию являются виртуальными, и я уверен, что оно выглядит аналогично и в C #.) В большом проекте это огромный выигрыш в обслуживании.

0 голосов
/ 30 января 2009

Как насчет:

/* Code Block 1... */

if(/* result of some condition or function call */)
{
   /* Code Block 2... */

   if(/* result of some condition or function call */)
   {
      /* Code Block 3... */

      if(/* result of some condition or function call */)
      {
         /* Code Block 4... */
      }
   }
}

Становится так:

/* Code Block 1... */
IsOk = /* result of some condition or function call */

if(IsOK)
{
   /* Code Block 2... */
   IsOk = /* result of some condition or function call */
}

if(IsOK)
{
   /* Code Block 3...*/
   IsOk = /* result of some condition or function call */
}

if(IsOK)
{
   /* Code Block 4...*/
   IsOk = /* result of some condition or function call */
}

/* And so on... */

Конечно, вы можете вернуться, если IsOk станет ложным, если это необходимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...