8

多くのチェックを実行する必要があり、実際に何かを実行する前に少なくとも 5 ~ 6 レベルインデントされているコードに遭遇することがよくあります。私はどのような代替案が存在するのだろうかと思っています。

以下に、私が話していることの例を投稿しました (これは実際の製品コードではなく、頭の中で思いついたものです)。

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 などの手続き型言語から来ています (is C procedural :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 に答える