オブジェクト指向プログラミング十戒 3 条件分岐を極力排除する


 

3 条件分岐を極力排除する

メソッドに分岐が多いとテストケースも増える。そして変更によってテストケースの改修も増えることになる。そこで比較アルゴリズム部分をクラスとして分離する。

Hexahedron.java
public class Hexahedron { // 直方体 protected int length; // 縦 protected int side; // 横 protected int height; // 高さ public Hexahedron(int length, int side, int height) { this.length = length; this.side = side; this.height = height; } }
Comparison.java
public class Comparison { public int compare(int type, Hexahedron h1, Hexahedron h2) { if (type == 0) { // 縦 if (h1.length > h2.length) return 1; else if (h1.length < h2.length) return -1; else return 0; } else if (type == 1) { // 横 if (h1.side > h2.side) return 1; else if (h1.side < h2.side) return -1; else return 0; } } }

2つの直方体の縦、横のどれかを比較するクラスを作成しました。どこを比較するかは最初の引数 type で指定します。

しかし、この後、高さも比較できるようにComparison クラスを変更したときは、Comparison クラスの試験を再度しなければならなくなります。

ICompare.java
public interface ICompare { public int compare(Hexahedron h1, Hexahedron h2); }
CompareLength.java
public class CompareLength implements ICompare { public int compare(Hexahedron h1, Hexahedron h2) { if (h1.length > h2.length) return 1; else if (h1.length < h2.length) return -1; else return 0; } }
CompareSide.java
public class CompareSide implements ICompare { public int compare(Hexahedron h1, Hexahedron h2) { if (h1.side > h2.side ) return 1; else if (h1.side < h2.side ) return -1; else return 0; } }
Comparison.java
public class Comparison { private ICompare comp; public Comparison(ICompare comp) { this.comp = comp; } public int compare(Hexahedron h1, Hexahedron h2) { return comp.compare(h1, h2); } }

この後、高さも比較できるように変更があったとしても CompareHeight クラスを追加するだけです。

既存のクラスには何も変更を加えていないので試験する必要がありません。


ネストを深くしない。早期に return できるのなら、それを検討する。

Person.java
public class Person { public void doSomething() { // 略 if (employee) { // 社員 if (yearsOfService >= 10) { // 勤続10年以上 if (dependents > 0) { // 扶養家族あり // 勤続10年以上で扶養家族のいる社員のとき // 略(長~~~~い処理) } } } return; } // 略 }

勤続10年以上で扶養家族のいる社員のときに何らかの処理をします。このように条件が多いほどネストがどんどん深くなってしまいます。

Person.java
public class Person { public void doSomething() { // 略 if (!employee) return; // 社員でない if (yearsOfService < 10) return; // 勤続10年未満 if (dependents == 0) return; // 扶養家族なし // 勤続10年以上で扶養家族のいる社員のとき // 略(長~~~~い処理) return; } // 略 }

条件に合わないことが分かった場合はすぐに return させ、ネストが深くならないようにします。


エラー処理を if 文ではなく、try ~ catch で行う。

Process.java
public class Process { public boolean proc() { if (proc1()) { if (proc2()) { if (!proc3()) { System.err.println("proc3 エラー"); return false; } } else { System.err.println("proc2 エラー"); return false; } } else { System.err.println("proc1 エラー"); return false; } return true; } private boolean proc1() { return true; } // 成功 private boolean proc2() { return true; } // 成功 private boolean proc3() { return false; } // 失敗 }

if 文でエラーかどうかの判定を行うと、本来の正常な処理の記述を複雑にしてしまいます。

また、エラーが起きるかもしれない処理とエラー処理が離れたところに記述されるため、対応が難しくなります。

Process.java
public class Process { public boolean proc() { try { proc1(); proc2(); proc3(); } catch(Exception e) { System.err.println(e.getMessage()); return false; } return true; } private void proc1() throws Exception {return;} //成功 private void proc2() throws Exception {return;} //成功 private void proc3() throws Exception { throw new Exception("proc3 エラー"); // 失敗 } }

正常な処理は try に、エラー処理は catch に記述します。これによって、処理が分離されて分かりやすくなります。