防御的プログラミング(CodeCompleteのまとめ)
CodeComplete 第8章防御的プログラミングのまとめ。
防御的プログラミングとは、「そうなるはずだ」と決めつけないこと、
防御運転にヒントを得たものである。
代わりに、「ゴミ入れ、なにも出さない」、「ゴミ入れ、エラーメッセージをだす」、「ゴミ入れ禁止」を採用する。
アサーションのガイドライン
堅牢性と正当性のどちらを担保するか最初に設計する。
そして上位レベルのエラー処理を設計する。
堅牢性とは、ソフトウェアの実行を継続するように手をつくすこと。
正当性とは、不正確な結果を決して返さないこと。
例外の利点を理解し、問題を避けるためのアドバイス。
手術室に例えると、外のものを消毒して内部に持ち込むこと。
入力データは入力されたらすぐに正しい型に変換する。
なぜなら、誰かがデータを変換してプログラムをクラッシュさせる危険が高まるため
バリケードの外側は何かを想定するのは危険なので、エラー処理を行う。
内側は安全なはずなので、アサーションを行う。
製品バージョンと開発バージョンは求めるパフォーマンスやリソースが異なるので、製品の制約を開発に当てはめない。
デバッグエイドの導入は早ければ早い方が良い。
攻撃的プログラミングを使う。
開発中に例外状況を明確にし、コードの実行中に回復できるようにする。
通常は、動かないプログラムのほうが欠陥のないプログラムよりもずっと問題は少ない。
攻撃的プログラミングのガイドライン。
デバッグエイドの削除計画を立てる。以下の方法が有効。
しかし製品段階ではエラーを目立たないようにしてプログラムを回復させるか上品に終わらせたい、という矛盾に応える
どれを製品コードに残すかのガイドライン。
あまりにもチェックが多いとコードは肥大化する。
そのため、優先順位を設定する
防御的プログラミングとは、「そうなるはずだ」と決めつけないこと、
防御運転にヒントを得たものである。
無効な入力への防御
「ゴミ入れ、ゴミ出し」を行うコードは書かない。代わりに、「ゴミ入れ、なにも出さない」、「ゴミ入れ、エラーメッセージをだす」、「ゴミ入れ禁止」を採用する。
ゴミ入れに対処する方法
- 外部ソースからのデータをすべて確認する
- ルーチン(外部ソースではない)のすべての入力値を確認する
- 不正な入力を処理する方法を検討する
- 8.3エラー処理テクニックの10のうちのいずれかの方法を採用
アサーション
アサーションは、大きくて複雑なプログラムや特に高い信頼性が求められるプログラムでは特に効果的。アサーションのガイドライン
- 予想される状況にはエラーコードを用い、予想されない状況にはアサーションを用いる
- アサーションに実行コードを埋め込まない
- なぜなら、デバッグモードをオフにした時にコンパイルされないことがある
- 事前条件と事後条件の文書化に使う
- ルーチンの最初にアサーションを定義する
- 堅牢性の高いコードはアサーションしてから処理する
エラー処理テクニック
エラー処理テクニックのガイドライン。- 当たり障りのない値を返す
- 次に有効なデータで代替する
- 前回と同じ値を返す
- 有効な値のうち、もっとも近いもので代用する
- ファイルに警告メッセージを記録する
- エラーコードを返す
- エラー処理ルーチンを呼び出す
- 発生した場所でエラーメッセージを出す
- ローカルでもっともうまくいく方法でエラーを処理する
- 処理を中止する
堅牢性と正当性のどちらを担保するか最初に設計する。
そして上位レベルのエラー処理を設計する。
堅牢性とは、ソフトウェアの実行を継続するように手をつくすこと。
正当性とは、不正確な結果を決して返さないこと。
例外
例外が正常な処理としてあつかわれているプログラムは可読性や保守性の問題から逃れられない。例外の利点を理解し、問題を避けるためのアドバイス。
- 本当に例外的状況のみエラーをスローする
- 例外を責任逃れて使用しない
- ローカルでキャッチできない例外をスローしてはいけない
- コンストラクタとデストラクタで例外をスローしない
- オブジェクトが完全に生成されていないとデストラクタできずメモリリークを発生させる
- 正しい抽象化レベルで例外をスローする
- そのクラスで発生する例外に具体化する
- 例外メッセージに原因となるすべてのメッセージを盛り込む
- 空のcatchブロックをかかない
- ライブラリコードがスローする例外を知る
- 例外レポート用ルーチンでの集中管理を検討する
- プロジェクトで例外の使用方法を標準化する
- 例外に変わる手段を検討する
- 最後に、プログラムに本当に例外処理が必要かを検討すること
バリケードによるエラーの被害の囲い込み
検証クラスによるバリケードを設けることで、正しいものが渡されていないかもしれない信頼できないクラスから、正しいものが渡されているとする信頼できるクラスへ渡す。手術室に例えると、外のものを消毒して内部に持ち込むこと。
入力データは入力されたらすぐに正しい型に変換する。
なぜなら、誰かがデータを変換してプログラムをクラッシュさせる危険が高まるため
バリケードの外側は何かを想定するのは危険なので、エラー処理を行う。
内側は安全なはずなので、アサーションを行う。
デバッグエイド
デバッグエイドとはデバッグを補助するツールで、エラーをすばやく検出するための心強い味方になる。製品バージョンと開発バージョンは求めるパフォーマンスやリソースが異なるので、製品の制約を開発に当てはめない。
デバッグエイドの導入は早ければ早い方が良い。
攻撃的プログラミングを使う。
開発中に例外状況を明確にし、コードの実行中に回復できるようにする。
通常は、動かないプログラムのほうが欠陥のないプログラムよりもずっと問題は少ない。
攻撃的プログラミングのガイドライン。
- アサーションを使ってプログラムを中断する
- メモリ割当のエラーを検出できるようにする
- ファイルフォーマットのエラーをすべて検出できるようにする
- 条件分岐のelse defaultを激しく失敗させる
- オブジェクトを削除するまえにジャンクデータを設定する
- エラーログファイルが自分あてにメールされるようにする
デバッグエイドの削除計画を立てる。以下の方法が有効。
- 製品版ではデバッグコードをとりのぞく
- プリプロセッサで簡単に切り替える
- デバッグ用のスタブを使用する
製品コードに防御的プログラミングをどれくらい残すか
開発段階ではエラーを見逃さないように醜悪に目立たせるようにしたい。しかし製品段階ではエラーを目立たないようにしてプログラムを回復させるか上品に終わらせたい、という矛盾に応える
どれを製品コードに残すかのガイドライン。
- 重要なエラーを検査するコードは残す
- ささいなエラーを検知するコードは削除する
- 処理を中断するコードは削除する
- 致命的なエラーを検出するデバッグコードが含まれるとき、プログラムを上品にクラッシュさせるコードがあれば残す
- テクニカルサポート編集者のためのエラーを記録する
- わかりやすいエラーメッセージは残す
防御的プログラミングに対する防御
複雑性が増え、そして防御的コードにもまたエラーの可能性を含むためあまりにもチェックが多いとコードは肥大化する。
そのため、優先順位を設定する