15

一見したところ、多数のネストされた if ステートメントを必要とする複雑なビジネス ロジックを処理するための適切な方法は何ですか?

例:

割引クーポン。になり得る:

1a) バリューディスカウント
1b) パーセンテージディスカウント

2a) 通常割引
2b) プログレッシブ割引

3a) アクセス クーポンが必要
3b) アクセス クーポンが不要

4a) 以前に購入した顧客のみに
適用 4b) すべての顧客に適用

5a) 国 (X、Y、…) のお客様のみに適用

これには、次のコードよりもさらに複雑なコードが必要です。

if (discount.isPercentage) {
    if (discount.isNormal) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    } else if (discount.isProgressive) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    }
} else if (discount.isValue) {
    if (discount.isNormal) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    } else if (discount.isProgressive) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    }
} else if (discount.isXXX) {
    if (discount.isNormal) {
    } else if (discount.isProgressive) {
    }
}

IF を switch/case に置き換えても、まだ複雑すぎます。読みやすく、保守しやすく、テストしやすく、理解しやすいものにする方法は何ですか?

4

9 に答える 9

14

良い質問。「条件付きの複雑さ」はコードの匂いです。ポリモーフィズムはあなたの友達です。

条件付きロジックは、理解が簡単で数行のコードに含まれている初期段階では無害です。残念ながら、それがうまく老化することはめったにありません。いくつかの新機能を実装すると、突然、条件付きロジックが複雑で拡張的になります。[Joshua Kerevsky: パターンへのリファクタリング]

if ブロックの入れ子を避けるためにできる最も簡単なことの 1 つは、Guard Clausesの使い方を学ぶことです。

double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};  

私が見つけたもう1つのことは、物事をかなり単純化し、コードを自己文書化するものであり、Consolidating conditionalsです。

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

条件式に関連するその他の有益なリファクタリング手法には、Decompose ConditionalReplace Conditional with VisitorReverse Conditionalなどがあります。

于 2009-10-22T13:43:41.663 に答える
10

仕様パターンはあなたが探しているものかもしれません。

概要:

コンピュータプログラミングでは、仕様パターンは特定のソフトウェア設計パターンであり、ブール論理を使用してビジネスロジックを連鎖させることにより、ビジネスロジックを再結合できます。

于 2009-10-22T13:38:59.817 に答える
6

比較対象のリストをフィードする汎用ステートマシンを作成します。

于 2009-10-22T13:39:02.337 に答える
2

あなたは本当に見るべきです

クリーンなコードトーク-継承、ポリモーフィズム、
MiškoHeveryによる

Google TechTalks2008年11月20日

概要

コードはifステートメントでいっぱいですか?ステートメントを切り替えますか?さまざまな場所で同じswitchステートメントがありますか?変更を加えるとき、複数の場所で同じif / switchに同じ変更を加えていることに気づきましたか?忘れたことがありますか?

この講演では、オブジェクト指向技術を使用してこれらの条件の多くを削除する方法について説明します。その結果、テスト、理解、保守が容易な、よりクリーンでタイトな、より適切に設計されたコードが得られます。

于 2011-03-25T18:34:54.877 に答える
2

それを行うオブジェクト指向の方法は、共通のインターフェースを実装する複数の割引クラスを持つことです。

dicsount.apply(order)

注文が割引の対象となるかどうかを判断するロジックを割引クラスに入れます。

于 2009-10-22T13:39:33.027 に答える
1

FWIW、私はこの種のことにHamcrestを非常にうまく使用しています。@Arnisが話した仕様パターンを実装していると言えると思います。

于 2009-10-22T14:03:32.480 に答える
0

私の最初の考えは、これはテスト可能ではなく、テスト可能にするために解決策につながるということです。

if (discount.isPercentage) {
  callFunctionOne(...);
} else if (discount.isValue) {
  callFunctionThree(...);
} else if (discount.isXXX) {
  callFunctionTwo(...);
}

次に、ネストされた各 if ステートメントを個別の呼び出しにすることができます。このようにして、それらを個別にテストできます。大規模なグループをテストすると、個々のグループが機能することがわかります。

于 2009-10-22T13:41:04.063 に答える
0

特定のケースをチェックするメソッドを作成します。

bool IsValueNormalAndRequiresCoopon(ディスカウント割引){...}

bool IsValueNormalAndRequiresCoupon(ディスカウント割引){...}

それを始めると、選択肢間の共通のロジックをどこで抽象化できるかが簡単にわかります。その後、そこから行くことができます。

複雑な決定の場合、可能な状態を処理するクラスで終わることがよくあります。

于 2009-10-22T13:42:18.077 に答える