Работа с вложенными, если затем else/вложенными операторами switch

Существуют ли какие-либо шаблоны/методы проектирования/способы удаления вложенных, если затем еще условия/инструкции switch?

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

Ответ 1

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

Или здесь другой пример

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

Изменить

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

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 объектом. Если у вас есть только несколько объектов, и они не меняются, это очень хорошо работает.

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

Ответ 2

Вы читали this о сглаживании кода стрелки из Coding Horror?

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

Ответ 3

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

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

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

  • Возьмите все ваши условные операторы и переместите их в какой-то "factory", который предоставит вам соответствующий тип во время выполнения.

  • Вы закончили. Не так ли?:)

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

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

Ответ 5

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

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, где методы по умолчанию по умолчанию, и я уверен, что он похож на С# тоже.) В большом проекте это огромный выигрыш в поддержке.

Ответ 6

Вы можете посмотреть на Strategy Pattern, в котором вместо того, чтобы ставить длинную цепочку ifs со связанными условиями, вы абстрагируете каждое условие к другому объекту, каждый из которых определяет, каково его конкретное поведение.

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

Ответ 7

Как насчет:

/* 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 станет ложным, если это необходимо.