8

我经常遇到必须执行大量检查的代码,最终在真正做任何事情之前至少要缩进五六级。我想知道存在哪些替代方案。

下面我发布了一个我正在谈论的示例(这不是实际的生产代码,只是我想出的东西)。

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;
}
4

7 回答 7

16

请参阅展平箭头代码以获取帮助。

  1. 用保护条款替换条件。
  2. 将条件块分解为单独的函数。
  3. 将否定检查转换为肯定检查。
于 2008-12-11T16:54:39.017 回答
8

早点回来:

if (input == null) {
    return output;
}
于 2008-12-11T16:54:44.087 回答
7

是的,有一个替代方案。

并且请永远不要这样编码(除非您正在维护自己的代码)

我不得不维护这样的代码,并且像 Charles_Bronsonn 的电影一样糟糕(尽管有些人喜欢那些电影)

无论如何,这种代码通常来自过程语言,例如 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/

于 2008-12-11T17:49:05.770 回答
2

是的,您可以按如下方式删除缩进:

基本上按顺序进行检查,并与失败而不是成功进行比较。它消除了嵌套并使其更易于遵循(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;
}
于 2008-12-11T16:56:36.127 回答
1

您可以使用保护子句摆脱一些嵌套。

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 "";不过,将我用来说明这一点的所有语句更改为引发描述性各种异常的语句。

于 2008-12-11T16:54:49.717 回答
1

如果您不需要处理停止,请不要嵌入。

例如,您可以这样做:

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 几乎是不可能的。

于 2008-12-11T16:58:19.053 回答
0

如果这只是一个可读性问题,您可以通过将嵌套移动到另一种方法来使其更清晰。如果您愿意,还可以转换为后卫风格。

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;
}
于 2008-12-11T17:08:21.187 回答