Вопрос

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

Ниже я разместил пример того, о чем говорю (это не настоящий производственный код, а просто то, что пришло мне в голову).

public String myFunc(SomeClass input)
{
    Object output = null;

    if(input != null)
    {
        SomeClass2 obj2 = input.getSomeClass2();
        if(obj2 != null)
        {
            SomeClass3 obj3 = obj2.getSomeClass3();
            if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
            {
                SomeClass4 = obj3.getSomeClass4();
                if(obj4 != null)
                {
                    int myVal = obj4.getSomeValue();
                    if(BAD_VALUE != myVal)
                    {
                        String message = this.getMessage(myVal);
                        if(MIN_VALUE <= message.length() &&
                           message.length() <= MAX_VALUE)
                        {
                            //now actually do stuff!
                            message = result_of_stuff_actually_done;
                        }
                    }
                }
            }
        }
    }
    return output;
}
Это было полезно?

Решение

Видеть Сглаживание кода стрелки для помощи.

  1. Заменить условия на охраняемые пункты.
  2. Разложить условные блоки на отдельные функции.
  3. Преобразовать отрицательные проверки в положительные проверки.

Другие советы

Возвращение раньше:

if (input == null) {
    return output;
}

Да, есть альтернатива.

И, пожалуйста, никогда не используйте такой код (если только вы не поддерживаете свой собственный код).

Мне приходилось поддерживать такой код, и он так же ужасен, как фильм Чарльза Бронсона (хотя некоторым людям нравятся эти фильмы)

Этот тип кода обычно исходит из процедурных языков, таких как C (это C процедурный: P). В любом случае.

Именно по этой причине ObjectOrientedProgrammng стал мейнстримом.Он позволяет создавать объекты и добавлять к ним состояние.Создайте операции с этим состоянием.Они не только владельцы собственности.

Я знаю, что вы придумали этот сценарий, но в большинстве случаев все эти условия бизнес правила!!.В большинстве случаев эти правила МЕНЯЮТСЯ, и если первоначального разработчика больше нет (или уже прошло несколько месяцев), не будет реального способа изменить этот код.Правила неудобно читать.И от этого возникает много боли.

Что ты можешь сделать?

1.) Сохраняйте состояние объекта ВНУТРИ объекта, используя частный переменные-члены (атрибуты AKA, свойства, переменные экземпляров и т. д.)

2.) Сделайте методы закрытыми (для этого и предназначен этот уровень доступа), чтобы никто не мог вызвать их по ошибке, и поместите программу в зону NullPointerException.

3.) Создайте методы, определяющие условие.Вот что они называют самодокументируемый код

Поэтому вместо

// validates the user has amount
if( amount > other && that != var || startsAligned() != false  ) {
}

Создать метод

if( isValidAmount() ) {
}

private boolean isValidAmount() {
   return ( amount > other && that != var || startsAligned() != false  );
}

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

Так как же будет выглядеть ваша гипервложенность при таком подходе?

Так.

// these are business rules
// then it should be clear that those rules are
// and what they do.

// internal state of the object.
private SomeClass2 obj2;
private SomeClass3 obj3;
private SomeClass4 obj4;

//public String myFunc( SomeClass input ) {
public String myComplicatedValidation( SomeClass input ) {
    this.input = input;
    if ( isValidInput() && 
        isRuleTwoReady() &&
        isRuleTreeDifferentOf( BAD_OBJECT ) &&
        isRuleFourDifferentOf( BAD_VALUE ) && 
        isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) { 
                message = resultOfStuffActuallyDone();
    }
}

// These method names are self explaining what they do.
private final boolean  isValidInput() {
    return  this.input != null;
}
private final boolean isRuleTwoReady() {
    obj2 = input.getSomeClass2();
    return obj2 != null ;
}
private final boolean isRuleTreeDifferentOf( Object badObject ) {
    obj3 = obj2.getSomeClass3();
    return obj3 != null && !badObject.equals( obj3.getSomeProperty() );
}
private final boolean isRuleFourDifferentOf( int badValue ) {
    obj4 = obj3.getSomeClass4();
    return obj4 != null && obj4.getSomeValue() != badValue;
}
private final boolean isMessageLengthInRenge( int min, int max ) {
    String message = getMessage( obj4.getSomeValue() );
    int length = message.length();
    return length >= min && length <= max;
}

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

    if ( isValidInput() && 
        isRuleTwoReady() &&
        isRuleTreeDifferentOf( BAD_OBJECT ) &&
        isRuleFourDifferentOf( BAD_VALUE ) && 
        isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) { 
                message = resultOfStuffActuallyDone();
    }

Может быть почти прочитано как

if is valid input 
and rule two is ready 
and rule three is not BAD OBJECT 
and rule four is no BAD_VALUE 
and the message length is in range

А если правила будут небольшими, программист сможет их очень легко понять и не бояться что-то тормозить.

Подробнее об этом можно прочитать здесь: http://www.refactoring.com/

Да, вы можете удалить отступы следующим образом:

В основном выполняйте проверки последовательно и сравнивайте их с неудачами, а не с успехами.Он удаляет вложенность и упрощает отслеживание (IMO).

public String myFunc(SomeClass input)
{
    Object output = null;

    if (input == null)
    {
        return null;
    }

    SomeClass2 obj2 = input.getSomeClass2();
    if (obj2 == null)
    { 
        return null;
    }

    SomeClass3 obj3 = obj2.getSomeClass3();
    if (obj3 == null || BAD_OBJECT.equals(obj3.getSomeProperty()))
    {
        return null;
    }

    SomeClass4 = obj3.getSomeClass4();
    if (obj4 == null)
    {
        return null;
    }
    int myVal = obj4.getSomeValue();
    if (BAD_VALUE == myVal)
    {
        return null;
    }
    String message = this.getMessage(myVal);
    if (MIN_VALUE <= message.length() &&
       message.length() <= MAX_VALUE)
    {
        //now actually do stuff!
        message = result_of_stuff_actually_done;
    }
    return output;
}

Если вам не нужно обрабатывать стоп, не встраивайте.

Например, вы можете сделать:

if(input == null && input.getSomeClass2() == null && ...)
    return null;

// Do what you want.

Предположим, вы используете такой язык, как Java, который упорядочивает условия.

В качестве альтернативы вы можете:

if(input == null && input.getSomeClass2() == null)
    return null;

SomeClass2 obj2 = input.getSomeClass2();
if(obj2 == null)
    return null;

...

// Do what you want.

Для более сложных случаев.

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

Вы можете избавиться от части вложенности, используя защитные предложения.

public String myFunc(SomeClass input)
{
    Object output = null;

    if(input == null) return "";

    SomeClass2 obj2 = input.getSomeClass2();
    if(obj2 == null) return "";

    SomeClass3 obj3 = obj2.getSomeClass3();
    if(obj3 == null || BAD_OBJECT.equals(obj3.getSomeProperty()))
    {
        return "";
    }

    SomeClass4 = obj3.getSomeClass4();
    if(obj4 == null) return "";

    int myVal = obj4.getSomeValue();
    if(BAD_VALUE == myVal) return "";

    String message = this.getMessage(myVal);
    if(MIN_VALUE <= message.length() &&
           message.length() <= MAX_VALUE)
    {
         //now actually do stuff!
         message = result_of_stuff_actually_done;
    }

    return output;
}

Измените все return ""; Тем не менее, операторы, которые я использовал для иллюстрации, указывают на операторы, которые выдают описательную разновидность Exception.

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

public String myFunc(SomeClass input)
{
    Object output = null;

    if (inputIsValid(input))
    {
      //now actually do stuff!
      message = result_of_stuff_actually_done;
    } 

    return output;
}


private bool inputIsValid(SomeClass input)
{

    // *****************************************
    // convert these to guard style if you like   
    // ***************************************** 
    if(input != null)
    {
        SomeClass2 obj2 = input.getSomeClass2();
        if(obj2 != null)
        {
            SomeClass3 obj3 = obj2.getSomeClass3();
            if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
            {
                SomeClass4 = obj3.getSomeClass4();
                if(obj4 != null)
                {
                    int myVal = obj4.getSomeValue();
                    if(BAD_VALUE != myVal)
                    {
                        String message = this.getMessage(myVal);
                        if(MIN_VALUE <= message.length() &&
                           message.length() <= MAX_VALUE)
                        {
                            return true;
                        }
                    }
                }
            }
        }
    }
    return false;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top