今回は場合分けによる変更コストとオブジェクト指向のメリットについてです。
例えば給付金が発生して、その金額を死亡時、退職時、通常時で場合分けするとき、if-elseで書けば下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
double func1(){ double result; if( isDead()) { result = deadAmount(); }else if( isRetired() ) { result = retiredAmount(); }else { result = normalAmount(); } return result; } |
この場合、早期リターンで書くと実はelseを書く必要はありません。
「死亡した時」はただちに「死亡時の金額」を返す、「退職した時」はただちに「退職時の金額」を返す、それ以外は「通常の金額」を返す。
金額が確定したらすぐにreturnをする場合のコードは下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
double func1(){ double result; if( isDead()) { return deadAmount(); }else if( isRetired() ) { return retiredAmount(); }else { return normalAmount(); } } |
これだけでもずいぶんすっきりしたコードになりましたが、さらにelse句を取り除くと、下記のように書けます。
1 2 3 4 5 6 7 8 |
double func1(){ double result; if( isDead()) return deadAmount(); if( isRetired()) return retiredAmount(); return normalAmount(); } |
特殊な場合をreturnで返し、else句を使わずに早期リターンするこの書き方をガード節といいます。
処理の独立性
ガード節と早期リターンを適用した最後の書き方は非常にコードがすっきりとわかりやすくなりました。
if節の中にさらにif節を書いていくような構造は非常にわかりにくくなります。単文を並べたコードのほうが見やすい処理となるでしょう。
また単文のほうが処理の独立性も高くなっています。
ここに離婚した時を加えても下記のように追加してあげればいいだけです。
1 2 3 4 5 6 7 8 9 |
double func1(){ double result; if( isDead()) return deadAmount(); if( isRetired()) return retiredAmount(); if( isSeparated()) return separatedAmount(); return normalAmount(); } |
この場合、3つの文はどこに入れてあげても処理が問題がありません。このように同じ場合分けでも、プログラムの影響度が低い単結合にすることがオブジェクト指向のアイディアです。
オブジェクト指向らしい場合分け
さらにオブジェクト指向を使った場合分けでは、場合ごとのロジック、それぞれ独立した別のクラスにすることが可能です。たとえば死亡時の金額を返すクラスDeadAmountクラスをつくってしまうのです。
1 2 3 |
class DeadAmount{・・・・・} class RetiredAmount{・・・・} class NormalAmount{・・・・・} |
この場合DeadAmountクラスを修正しても、他のクラスに影響がでません。場合ごとの処理が完全に独立されており、影響度が限定されるのです。
ただし、場合ごとにクラスを分けると複数のクラスを使い分けるのは大変になります。そこで3つの場合ごとのクラスの違いを意識せずに仕組みがインターフェイス宣言です。
1 2 3 4 5 6 7 |
interface PayAmount{ double getAmount(); } class DeadAmount implements PayAmount{・・・・・} class RetiredAmount implements PayAmount {・・・・} class NormalAmount implements PayAmount {・・・・・} |
給付金額を知りたいクライアントクラスは下記のようになります。
クラス図で書くと下記の通りです。
1 2 3 4 5 6 7 |
class Client{ private PayAmount amount; double getAmount(){ return amout.getAmount(); } } |
クラスクライアントはPayAmountという型を意識しているだけで死亡時、退職時、通常時の場合分けは意識していません。
しかしPayAmount型で参照する実装クラスのオブジェクトを静止するときに、ifかswitchが必要になります。Javaの場合はenumという便利な仕組みがあるため、ifなしで下記のように記述することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
enum PaymentType { dead ( new DeadAmount()), retired ( new Retired()), normal( new NormalAmount()) private PayAmount amount; private PaymentType(Payamount amount) { this.amount = amount; } double getAmount(){ return amount.getAmount(); } } |