有没有替代超级缩进代码的方法?

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;
}
7个回答

16

参考《展开箭头代码》以获得帮助。

  1. 用保护条款替换条件。
  2. 将条件块分解成单独的函数。
  3. 将负面检查转换为正面检查。

8

尽早返回:

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

7

是的,有一个替代方案。

请不要编写那样的代码(除非您在维护自己的代码)。

我曾经不得不维护过那样的代码,就像查尔斯·布朗森(Charles Bronson)的电影一样糟糕(尽管有些人喜欢这些电影)。

这种代码通常来自过程性语言,例如 C(C 是过程性的 :P)。无论如何,这就是面向对象编程成为主流的原因。它允许您创建对象并向其中添加状态。使用该状态创建操作。它们不仅仅是属性持有者。

我知道您编造了那种情景,但大多数情况下所有这些条件都是 业务规则!!。这些规则大多数情况下会发生变化,如果原始开发人员已经离开(或已经过去几个月),则将没有可行的方法修改该代码。这些规则很难阅读。这会带来大量痛苦。

你可以做什么?

1.) 使用 private 成员变量(也称为属性、实例变量等)将对象的状态保留在对象内部。

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/


2

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

基本上按顺序进行检查,并针对失败而不是成功进行比较。它会去除嵌套并使其更易于跟踪(在我看来)。

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测试处停止了?通过将De Morgan定律应用于反转复合测试的意义,它也可以成为早期返回。 - Hudson
你假设我知道德摩根定律是什么 :) 实际上,我之所以保留它,纯粹是出于个人喜好。在我的脑海中,这样阅读更有意义。 - Andrew Rollings

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语句几乎不可读。


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 "";语句更改为抛出各种描述性异常的语句。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接