SSブログ

防御的プログラミングとカバレッジ [プログラマー現役続行]

防御的プログラミングでは、外部からの呼び出しから防御します。しかし、それ以外に、自分自身の誤りに対して防御するということがあります。

次のコードは、『Effective Java 第2版』(p.147)に掲載されているコードです。
// 値によって切り替えるenum 型- 問題が多い
public enum Operation {
    PLUS, MINUS, TIMES, DIVIDE;

    // 定数で表される算術操作を行う
    double apply(double x, double y) {
        switch(this) {
            case PLUS:   return x + y;
            case MINUS:  return x - y;
            case TIMES:  return x * y;
            case DIVIDE: return x / y;
        }
        throw new AssertionError("Unknown op: " + this);
    }
}
ここで、注目するのは、最後のAssertionErrorをスローしている部分です。定義したenumの定数値はすべてcaseラベルで列挙されていますので、例外がスローされることはありません。

後々の保守や機能追加で定数値が追加され、caseラベルの追加を忘れた場合に、そのことを検出するための防御的プログラミングとなります。つまり、switchで、defaultラベルを書いて普通の処理をするのではなく、すべてcaseラベルで列挙し、defaultラベルではAssertionErrorをスローすることで、「設計上、起きてはいけないことが起きている」ことを検出できるようにしておきます。

このようなプログラミングは、呼び出しパラメータの不正値の検査と同様に、障害を短時間で調査するためには重要です。

一方、このような防御的プログラミングを行っていないソフトウェア開発組織では、テストによるコードカバレッジは、単体テストでは100%でなければならないという目標値を設定してしまいます。当然、上記のコードでは100%は無理です。AssertionErrorをスローしている部分は、どんなに頑張っても実行することはできません。

ソフトウェアの開発効率、特に、障害の短時間での原因調査と対策を行うのに有効なのはコードカバレッジ100%ではありません。有効なのは防御的プログラミングです。上記のコードで、カバレッジを100%にするために、applyメソッドが次のよう書かれたとしてみてください。
    // 定数で表される算術操作を行う
    double apply(double x, double y) {
        switch(this) {
            case PLUS:   return x + y;
            case MINUS:  return x - y;
            case TIMES:  return x * y;
            default:
                       return x / y;
        }
    }
enum定数値を追加し、caseラベルの追加を忘れても、コードカバレッジ100%なのです。そして、caseラベルの追加を忘れで障害が発生しても、すぐには原因が分からないかもしれません。

コードカバレッジ100%を常に達成していますと言う開発組織があったとしたら、このような防御的プログラミングは一切行っていないことを意味すると思います。
nice!(1)  コメント(0)  トラックバック(0) 

nice! 1

コメント 0

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

Facebook コメント

トラックバック 0