是否有超缩进代码的替代方案?

发布于 2024-07-10 08:13:44 字数 1152 浏览 8 评论 0原文

我经常遇到必须执行大量检查的代码,并且在真正执行任何操作之前最终会缩进至少五到六级。 我想知道还有哪些替代方案。

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

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

I often run into code that has to perform lots of checks and ends up being indented at least five or six levels before really doing anything. I am wondering what alternatives exist.

Below I've posted an example of what I'm talking about (which isn't actual production code, just something I came up with off the top of my head).

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

青春有你 2024-07-17 08:13:44

请参阅扁平化箭头代码获取帮助。

  1. 用守卫替换条件
    条款。
  2. 将条件块分解为
    独立的功能。
  3. 将负面支票转换为
    积极检查。

See Flattening Arrow Code for help.

  1. Replace conditions with guard
    clauses.
  2. Decompose conditional blocks into
    seperate functions.
  3. Convert negative checks into
    positive checks.
我也只是我 2024-07-17 08:13:44

提前返回:

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

Return early:

if (input == null) {
    return output;
}
梓梦 2024-07-17 08:13:44

是的,还有另一种选择。

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

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

这种代码通常来自过程语言无论如何,比如 C (是 C 程序性的:P)。

这就是面向对象编程成为主流的原因。 它允许您创建对象并向它们添加状态。 使用该状态创建操作。 他们不仅仅是财产所有者。

我知道你编造了这个场景,但大多数时候所有这些条件都是业务规则!!。 大多数时候,这些规则会发生变化,如果原始开发人员不再存在(或者已经过去几个月),则不会有可行的方法来修改该代码。 规则读起来很困难。 很多痛苦都来自于此。

你能做什么?

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

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/

Yes there is an alternative.

And please never code like that ( unless you're maintaining your own code )

I have had to maintain code like that and is as awful as a Charles_Bronsonn film ( some people like those films though )

This kind of code is usual comming from procedural languages such as C ( is C procedural :P ) Anyway.

That was the reason why ObjectOrientedProgrammng became mainstream. It allows you to create objects and add state to them. Create operations with that state. They're not only property holders.

I know you made up that scenario but most of the times all those conditions are business rules!!. Most of the times those rules CHANGE, and if the original developer is not longer there ( or a couple of months have already passed ) there won't be a feasible way to modify that code. The rules are awkward to read. And a lot of pain comes from that.

What can you do?

1.) Keep the state of the object INSIDE the object using private member variables ( AKA attributes, properties, instances vars etc. )

2.) Make the methods private ( that's what that access level is for ) so none can call them by mistake and put the program in the NullPointerException land.

3.) Create methods that define what the condition is. Thats what they call self documenting code

So instead of

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

Create a method

if( isValidAmount() ) {
}

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

I know it looks verbose, but allows human be able to read the code. The compiler does not care about readability.

So how would it look like your hypernested with this approach?

Like this.

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

I know, It looks like more coding. But think about this. The rules are almost human readable

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

May be almost read as

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

And by keeping the rules vary small, the coder may understand them very easily and not be afraid of brake something.

A lot more can be read about this at: http://www.refactoring.com/

不再见 2024-07-17 08:13:44

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

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

Yes, you could remove the indents as follows:

Basically do the checks sequentially, and compare against failure rather than success.
It removes the nesting and makes it easier to follow (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;
}
小姐丶请自重 2024-07-17 08:13:44

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

例如,您可以这样做:

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 几乎无法读取。

If you don't need to process stop, don't embed.

For example, you can do:

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

// Do what you want.

Assuming you're using a language like Java that orders the conditionals.

Alternatively you could:

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

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

...

// Do what you want.

For more complex cases.

The idea is to return from the method if you don't need to process. Embedding in a large nested if is almost impossible to read.

原来分手还会想你 2024-07-17 08:13:44

您可以使用保护子句来消除一些嵌套。

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

You can get rid of some of the nesting by using guard clauses.

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

Change all of the return ""; statements that I used to illustrate the point to statements that throw a descriptive variety of Exception, though.

々眼睛长脚气 2024-07-17 08:13:44

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

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

if it's just a readability issue you could make it clearer by moving the nesting to another method. Additionally convert to guard style if you like.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文