この種の問題について、Martin Fowlerは仕様パターンを提案しました。
...設計パターン。ブールロジックを使用してビジネスルールをチェーン化することにより、ビジネスルールを再結合できます。
仕様パターンは、他のビジネスルールと組み合わせることができるビジネスルールの概要を示します。このパターンでは、ビジネスロジックのユニットは、その機能を抽象集約Composite Specificationクラスから継承します。Composite Specificationクラスには、ブール値を返すIsSatisfiedByという関数が1つあります。インスタンス化後、仕様は他の仕様と「連鎖」し、新しい仕様を簡単に保守可能にする一方で、高度にカスタマイズ可能なビジネスロジックを作成します。さらに、インスタンス化の際、ビジネスロジックは、メソッドの呼び出しまたは制御の反転により、永続リポジトリなどの他のクラスのデリゲートになるために状態を変更できます...
上記は少々高額に聞こえますが(少なくとも私には)、コードで試してみると非常にスムーズになり、実装と読み取りが簡単になりました。
私の見方では、主なアイデアは、チェックを専用のメソッド/オブジェクトに行うコードを「抽出」することです。
あなたのnetWorth
例では、これは次のようになります。
int netWorth(Person* person) {
if (isSatisfiedBySpec(person)) {
return person->assets - person->liabilities;
}
log("person doesn't satisfy spec");
return -1;
}
#define BOOLEAN int // assuming C here
BOOLEAN isSatisfiedBySpec(Person* person) {
return Person != NULL
&& person->isAlive
&& person->assets != -1
&& person->liabilities != -1;
}
あなたのケースはかなり単純に見えるので、すべてのチェックは単一のメソッド内の単純なリストに収まるように見えます。読みやすくするために、多くの場合、より多くのメソッドに分割する必要があります。
私は通常、「spec」関連メソッドを専用オブジェクトにグループ化/抽出しますが、それがなくても問題はありません。
// ...
Specification s, *spec = initialize(s, person);
if (spec->isSatisfied()) {
return person->assets - person->liabilities;
}
log("person doesn't satisfy spec");
return -1;
// ...
Stack Overflowのこの質問では、上記の仕様パターンの例に加えて、いくつかのリンクを推奨しています
。特に、回答では、例のウォークスルーのためにDimecastsの「Learning the Specificationパターン」を提案し、Eric EvansとMartin Fowlerが執筆した「Specifications」ペーパーに言及しています。