Как избежать множественных вложенных IF - PullRequest
19 голосов
/ 08 сентября 2010

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

У меня довольно много вложенных IF-операторов и я хочу от них избавиться. Как я могу пойти по этому поводу? Мой первый подход состоял в том, чтобы сделать это с исключениями, например,

public static Boolean MyMethod(String param) {
 if (param == null)
  throw new NullReferenceException("param may not be null");

 if (param.Equals("none") || param.Equals("0") || param.Equals("zero"))
  throw new ArgumentNullException("param may not be zero");

 // Do some stuff with param
 // This is not executed if param is null, as the program stops a soon
 // as one of the above exceptions is thrown
}

Этот метод используется в основном классе приложения, например,

static void Main() {
 try {
  Boolean test = MyClass.MyMethod(null); // Will throw an exception
 } catch (Exception ex) {
  MessageBox.Show(ex.Message, "Error");
 }

Я думаю, что это довольно хорошо, так как предотвращает вложенные операторы, и почти все действия методов хорошо расположены на одном уровне.

Как и в случае IF-операторов, метод будет выглядеть следующим образом

public Boolean MyMethod(String param) {
 if (param != null) {
  if (!param.Equals("none") && !param.Equals("0") && !param.Equals("zero")) {
   // Do some stuff with param
  } else {
   MessageBox.Show("param may not be zero", "Error");
 } else {
  MessageBox.Show("param may not be null", "Error");
 }
}

Что я нахожу очень, очень уродливым и трудно поддерживать.

Теперь вопрос в том, этот подход хорошо ? Я знаю, что это может быть субъективно, но как вы преодолеваете вложенные IF (1 или 2 уровня не так уж и плохо, но после этого становится хуже ...)

Ответы [ 6 ]

18 голосов
/ 08 сентября 2010

Ваша проблема известна как анти-паттерн стрелки.

Существуют прагматичные подходы, такие как заявления Guard, которые вы показали в своем образце, ко всем шаблонам проектирования, избегая, если (и еще) все вместе ...

Много ресурсов о том, как их решить:

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

http://www.lostechies.com/blogs/chrismissal/archive/2009/05/27/anti-patterns-and-worst-practices-the-arrowhead-anti-pattern.aspx

http://elegantcode.com/2009/08/14/observations-on-the-if-statement/

16 голосов
/ 08 сентября 2010

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

В случае использования блоков if для управления потоком метода (а не для принудительного выполнения контракта) иногда это может бытьнемного сложнее.Иногда я сталкиваюсь с кодом, подобным следующему (чрезвычайно упрощенному) примеру:

private void SomeMethod()
{
    if (someCondition == true)
    {
        DoSomething();
        if (somethingElse == true)
        {
           DoSomethingMore();
        }
    }
    else
    {
        DoSomethingElse();
    }
}

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

private void SomeMethod()
{
    if (someCondition == true)
    {
        DoItThisWay();
    }
    else
    {
        DoSomethingElse();
    }
}

private void DoItThisWay()
{
    DoSomething();
    if (somethingElse == true)
    {
       DoSomethingMore();
    }
}

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

4 голосов
/ 08 сентября 2010

Вы хотите исследовать контракты кода C # 4 .

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

1 голос
/ 08 сентября 2010

Может быть AspectF может помочь вам в этом случае:

public Boolean MyMethod(String param) {
 try {
  AspectF.Define
   .ErrorMsgIfNull(param, "must be not null")
   .ErrorMsgIfEquals(new string[] {"None", "Zero", "0"}, "may not be zero")
   //...
   // use your own "AspectFlets" you wrote
   //...
   .Do(() =>
    {
     // Do some stuff with param
     // This is not executed if param is null, as the program stops a soon
     // as one of the above exceptions is thrown
    });
}

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

Методы в приведенном выше коде являются лишь примером, который не существует, но который может быть легко реализован.

1 голос
/ 08 сентября 2010

Я думаю, что это будет полезно для вас:
http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism

0 голосов
/ 08 сентября 2010

Это действительно довольно широкий вопрос, на который нужно ответить, поскольку он действительно зависит от функциональности операторов if.

Конечно, где это возможно, я пытаюсь заменить вложенные операторы if вместо этого переключателем, но это не всегда возможно.

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

...