ジャトミカ学習帳

プログラミングとかデジタル信号処理とかのメモ書き

「ガード節」をいつ使うか

名著と言われている「リーダブルコード」を読んでいるので、気になった項目をピックアップしながら自分なりの考察を加えていこうと思います。 基本的に本の内容をそのまま引用しないようにしています。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

ガード節とは

関数の先頭で例外をはじく処理。ガード節という言葉がリーダブルコードで命名されたものなのか、プログラミングにおける一般的な用語なのかは、よく知りません。

1. あり得ない値をガードする
void calcArea(float width, float depth){
    if(width < 0 || depth < 0) return; /* ガード節 */
    ... /* メインの処理 */
}

関数によっては利用可能な引数の範囲が制限されていたりします。
たとえば100個のデータセットしかない配列で引数に応じて配列の参照位置を変える場合は、「0から99までの引数で呼ばれること前提」になります。
関数を呼ぶ側で適切な手当てがあれば問題ないのですが、そちらの実装担当が別の方だった場合は上手く連携を取れていないとバグになり得ます。

ではこのガード節を使えば解決、という簡単な話でもなく、問題点がいくつかあります。

  1. コードの見栄えが悪くなる
  2. ガードしたことに気付かない
  3. 処理が(多少)遅くなる

上記のコード例でいう ”メインの処理” の部分が1行で書ける関数の場合、ガード節を挟んでいくだけで処理の行数が2倍に膨らんでいきます。 規模の小さな関数にも全てガード節を挟んでいったら、見栄えはかなり悪くなってしまいます。

2点目について、ただ単にreturnするだけでは実際にバグが出ていても見落としてしまいます。 何かエラーメッセージを表示するなどでエラーを明示的に表示すれば見落としませんが、そうすると1点目の問題と同じでますます見栄えは悪くなっていきます。

周辺の関数とは明らかに引数の範囲が異なる場合など、「なんとなくバグが出そう」というところに挟んでいくのが実用上良さそうです。 また実行速度を優先する場合、ガードされることなくテストにクリアしたら製品リリース前にガード節を取っ払ってしまっても構わないでしょう。

2. 必要な情報が揃うまでガードする
ClassA(){ // コンストラクタ
    this->mWidth = -1;
    this->mDepth = -1;
}

void ClassA::setWidth(float width){
    this->mWidth = width; // メンバ変数へ保持
    calcArea();
}
void ClassA::setDepth(float depth){
    this->mDepth = depth; // メンバ変数へ保持
    calcArea();
}
void ClassA::calcArea(void){
    if(this->mWidth < 0 || this->mDepth < 0) return; /* ガード節 */
    ... /* メインの処理 */
}

こちらのケースは非常に有効的です。
何度も似たような計算を繰り返すことなく、必要な情報が全て揃ってから1回のみ計算するだけなので、メインの処理の負荷が重ければ重いほど、もしくは必要な情報が多ければ多いほど効果を発揮します。
また、テスト後に取っ払うわけではなくリリース後にも当然残る処理です。