Как лучше всего заменить или заменить if..else if..else деревья в программах? - PullRequest
33 голосов
/ 06 февраля 2009

Этот вопрос мотивирован тем, что я в последнее время слишком часто вижу, структурой if..else if..else. Несмотря на то, что он прост и имеет свое применение, что-то в нем постоянно повторяет мне, что его можно заменить чем-то более тонким, элегантным и просто в целом более актуальным.

Чтобы быть как можно более конкретным, вот что я имею в виду:

if (i == 1) {
    doOne();
} else if (i == 2) {
    doTwo();
} else if (i == 3) {
    doThree();
} else {
    doNone();
}

Я могу придумать два простых способа переписать это, либо троичным (это просто еще один способ написания той же структуры):

(i == 1) ? doOne() : 
(i == 2) ? doTwo() :
(i == 3) ? doThree() : doNone();

или используя Map (на Java и я тоже думаю на C #), Dictionary или любую другую структуру K / V, например:

public interface IFunctor() {
    void call();
}

public class OneFunctor implemets IFunctor() {
    void call() {
        ref.doOne();
    }
}

/* etc. */    

Map<Integer, IFunctor> methods = new HashMap<Integer, IFunctor>();
methods.put(1, new OneFunctor());
methods.put(2, new TwoFunctor());
methods.put(3, new ThreeFunctor());
/* .. */
(methods.get(i) != null) ? methods.get(i).call() : doNone();

На самом деле метод Map, описанный выше, это то, чем я занимался в прошлый раз, но теперь я не могу перестать думать, что для этой конкретной проблемы в целом должны быть лучшие альтернативы.

Итак, какие другие - и, скорее всего, лучшие - способы заменить if..else if .. еще там, и какой из них ваш любимый?

Ваши мысли под этой строкой!


Хорошо, вот ваши мысли:

Во-первых, самым популярным ответом был оператор switch, например:

switch (i) {
    case 1:  doOne(); break;
    case 2:  doTwo(); break;
    case 3:  doThree(); break;
    default: doNone(); break;
}

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

Другой и, возможно, более причудливый способ, которым вы, похоже, предлагаете, - это сделать это с помощью полиморфизма. Лекция на Youtube, связанная с CMS, - это отличные часы, посмотрите ее здесь: «Чистые разговоры по коду - наследование, полиморфизм и тестирование» Насколько я понял, это будет примерно так:

public interface Doer {
    void do();
}

public class OneDoer implements Doer {
    public void do() {
        doOne();
    }
}
/* etc. */

/* some method of dependency injection like Factory: */
public class DoerFactory() {
    public static Doer getDoer(int i) {
        switch (i) {
            case 1: return new OneDoer();
            case 2: return new TwoDoer();
            case 3: return new ThreeDoer();
            default: return new NoneDoer();
        }
    }
}

/* in actual code */

Doer operation = DoerFactory.getDoer(i);
operation.do();

Два интересных момента из Google Talk:

  • Использовать нулевые объекты вместо возврата нулей (и, пожалуйста, выбрасывайте только исключения времени выполнения)
  • Попробуйте написать небольшой проект без if: s.

Кроме того, на мой взгляд, стоит упомянуть один пост - CDR, который предоставил нам свои извращенные привычки, и хотя его не рекомендуется использовать, просто очень интересно посмотреть.

Спасибо всем за ответы (пока), думаю, я мог бы кое-что узнал сегодня!

Ответы [ 21 ]

21 голосов
/ 06 февраля 2009

Эти конструкции часто можно заменить полиморфизмом. Это даст вам более короткий и менее хрупкий код.

11 голосов
/ 06 февраля 2009

В объектно-ориентированных языках обычно используется полиморфизм для замены if.

Мне понравился Google Clean Code Talk, который охватывает тему:

Разговоры о чистом коде - наследование, полиморфизм и тестирование

РЕЗЮМЕ

Ваш код полон операторов if? Сменить заявления? У тебя есть один и тот же оператор переключения в различных места? Когда вы вносите изменения, вы обнаруживаете, что делаете то же самое изменение к тому же if / switch в нескольких места? Вы когда-нибудь забывали один?

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

11 голосов
/ 06 февраля 2009

Оператор переключения:

switch(i)
{
  case 1:
    doOne();
    break;

  case 2:
    doTwo();
    break;

  case 3:
    doThree();
    break;

  default:
    doNone();
    break;
}
5 голосов
/ 06 февраля 2009

Этот вопрос состоит из двух частей.

Как отправить на основе значения? Используйте оператор switch. Он отображает ваши намерения наиболее четко.

Когда для отправки на основе значения? Только в одном месте для каждого значения: создайте полиморфный объект, который знает, как обеспечить ожидаемое поведение для значения.

5 голосов
/ 06 февраля 2009

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

class iBase
{
   virtual void Foo() = 0;
};

class SpecialCase1 : public iBase
{
   void Foo () {do your magic here}
};

class SpecialCase2 : public iBase
{
   void Foo () {do other magic here}
};

Тогда в вашем коде просто вызовите p-> Foo (), и все произойдет правильно.

3 голосов
/ 06 февраля 2009

Вне использования оператора switch, который может быть быстрее, нет. Если остальное понятно и легко читается. необходимость искать вещи на карте запутывает вещи. Зачем затруднять чтение кода?

3 голосов
/ 06 февраля 2009

В этом простом случае вы можете использовать переключатель.

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

Полиморфизм был бы вариантом, если бы не слишком много случаев, а условия и поведение были нерегулярными.

3 голосов
/ 06 февраля 2009
switch (i) {
  case 1:  doOne(); break;
  case 2:  doTwo(); break;
  case 3:  doThree(); break;
  default: doNone(); break;
}

Набрав это, я должен сказать, что в вашем утверждении if не так уж много неправильного. Как сказал Эйнштейн: «Сделай это как можно проще, но не проще».

3 голосов
/ 06 февраля 2009

Используйте переключатель / чехол, он чище: p

3 голосов
/ 06 февраля 2009

Выражение switch , конечно, намного красивее, чем все, если и другие.

...