【上巻】Codeコンプリート第8, 9, 10章レビューメモ

【上巻】Codeコンプリート熟読中のメモになります。

かなり本書の文になるのでまずいようならご連絡頂ければと思います。

第8章 防御的プログラミング

  •  「そうなるはずだ」と決めつけない 「そうなるはずだ」と決めつけない
  • プログラムには必ず問題があり、プログラムは変更されるものされるものであり、賢いプログラマはそれを踏まえてコードを開発する
  • 製品版のコードでは、「ごみ入れ、ごみ出し」よりも洗練された方法で、エラーを処理すべきである
    • 良いプログラムは何を入れても決してゴミを出さない
      • ここでのゴミとは想定外の入力値や引数などのプログラムが受け取る値の事
    • 良いプログラムは「ゴミ入れ、何も出さない」「ゴミ入れ、エラーメッセージ出し」または「ゴミ入れ禁止」を採用する
  • 防御的プログラミングテクニックは、エラーの検出と修正を容易にし、製品版のコードへの被害を食い止める
  • 特に大規模なシステム、高い信頼性が要求されるシステム、変化の速いコードベースでは、アサーションはエラーを早期に検出するのに役立つ
    • 発生が予想される状況にはエラー処理コードを使用し、発生してはならない状況にはアサーションを使用する
    • エラー処理コードは、正常でない状態が発生していないかどうかを確認する。
      • 正常でない状態とは、それほど頻繁には発生しないものの、コードの作成者がそれを予測していて、製品版のコードで対処する必要がある状態である。
    • アサーションは、絶対に発生すべきでない状況が発生していないかどうかを確認する。一般に、エラー処理では不正な入力データを確認し、アサーションではコードのバグを確認する。
  • 不正な入力に対処する方法を決定することは、エラー処理においても上位レベルの設計においても重要な決断である
    • エラー処理テクニック
      • 当たり障りのない値を返す
      • 次に有効なデータで代用する
      • 前回と同じ答えを返す
      • 有効な値のうち、最も近いもので代用する
        • 筆者の車はバックするときにこの方法でエラーに対処している。速度計にはマイナスの速度は表示されないため、バックするときの速度表示は0(有効な値のうち、最も近いものになる)
        • 確かにバックの時の速度計は0だった気がするが、そもそもバックをマイナスとしている所も面白い。
      • ファイルに警告メッセージを記録する
      • エラーコードを返す
      • エラー処理ルーチン/オブジェクトを呼び出す
      • エラーが発生した場所でエラーメッセージを表示する
      • ローカルで最もうまくいく方法でエラーを処理する
      • 処理を中止する
  • 例外は、コードの正常な流れとは別の次元で、エラー処理を実行するための手段となる。慎重に使用すれば、例外はプログラマの知的な道具箱になくてはならないツールとなるが、他のエラー処理テクニックと比較検討すべきである
    • 例外の基本構造
      • ルーチンがthrowを使って例外オブジェクトをスローすると、呼び出し階層の上位にある他のルーチンのコードが、try-catchブロックを使って例外をキャッチする。
    • 無視すべきでないエラーは例外を使用してプログラムの他の部分に伝える
      • 例外の最大の利点は、無視できないような方法でエラー状態を知らせる事である。
    • 本当に例外的な状況でのみ例外をスローする
      • 例外はアサーションと同じような状況で使用される。
      • 稀にしか発生しないイベントではなく、[* 絶対に]発生してはならないイベントで使用する
  • 製品版のシステムに適用される制約を開発バージョンのシステムに適用する必要はない。開発バージョンは自分の都合の良いように使用することができ、エラーを素早く暴き出すためのコードを追加してもかまわない
    • 防御的プログラミングに対する防御
      • 防御的プログラミングを徹底しすぎるとエラーを生む可能性も増えるし、コードが肥大化し複雑になる
      • 防御的プログラミングすべき箇所とそうでない箇所に優先順位を設定する
      • 防御的プログラミングチェックリスト
      • 全般
        • ルーチンは不正な入力データから自身を保護するか。
        • アサーションを使って、事前条件と事後条件を含め、条件を文書化しているか。
        • アサーションの使用は絶対に発生すべきでない条件の文書化に限定しているか。
        • アーキテクチャや上位レベルの設計で、エラーの処理方法として特定のものを指定しているか。
        • アーキテクチャや上位レベルの設計で、エラー処理では堅牢性と正当性のどちらを優先すべきかを指定しているか。
        • バリケードを築いてエラーによる被害を食い止め、エラー処理を配慮しなければならないコードを減らしているか。
        • コードにデバッグエイドを使用しているか。
        • デバッグエイドは手間をかけずにオン/オフできるような方法で導入されているか。
        • 防御的プログラミングのコードの量は適切か(多すぎても少なすぎても良くない)
        • 開発段階でのエラーの見落としを防ぐために、防御的プログラミングテクニックを採用しているか。
      • 例外
        • プロジェクトで例外処理のための標準手法を定義しているか。
        • 例外に代わる方法を検討しているか。
        • エラーはなるべくローカルで処理し、例外をスローしないようにしているか。
        • コンストラクタやデストラクタで例外をスローしていないか。
        • すべての例外の抽象化レベルが、それらをスローするルーチンにとって適切か。
        • それぞれの例外に、その例外に関する適切な背景情報がすべて含まれているか。
        • コードに空のcatchブロックが含まれていないか(あるいは、空のcatchブロックを使用するのが妥当であれば、そのことが文書化されているか)。
      • セキュリティ
        • 不正なデータを検査するコードは、バッファオーバーフロー、こっそり仕込まれたSQLコマンドやHTMLコード、整数の桁あふれ、その他の悪質な入力の試みをチェックしているか。
        • エラーを返すコードをすべてチェックしているか。
        • すべての例外をキャッチしているか。
        • エラーメッセージがシステムへの侵入を手助けするような情報を提供していないか。

第9章 擬似コードによるプログラミング

  • クラス作成手順
    • クラス手順イメージ
  • ルーチン作成手順
  • 疑似コードプログラミングプロセス(PPP)
    • 擬似コードを効果的に使用するためのガイドライン
      • 特定の処理を正確に説明する文章を使用する。
      • プログラミング言語の構文要素を使用しない。擬似コードを使用すれば、コードそのものよりも少し上位レベルで設計を行うことが可能になる。プログラミング言語の構文を使用すると、詳細レベルに下がってしまうので、少し高いレベルで設計するという利点がなくなってしまうし、意味もなく構文上の制限に縛られてしまう。
      • 目的のレベルで擬似コードを書く。その方法をプログラミング言語でどのように実装するかではなく、その方法をとることの意味を説明する。
      • ほぼそのままコーディングできるくらいの詳細レベルで擬似コードを書く。擬似コードのレベルが概略すぎると、問題をはらんでいる部分がうまくごまかされてしまうおそれがある。コードを簡単に書き起こせるように見えるまで、擬似コードの細部を煮詰めていく。
      • 擬似コードを書いたら、それを基にコードを作成し、擬似コードはプログラミング言語のコメントとして添える。こうすると、コメントを作成する手間がほとんど省ける。ガイドラインに従って擬似コードを作成すれば、コメントは完璧に意味の通るものとなる。
    • 擬似コードプログラミングプロセス
      • 準備が完了していることを確認したか。
      • クラスで解決する問題を定義したか。
      • 概略レベルの設計は、クラスと各ルーチンの良い名前名前が思い浮かぶほど明確なものになっているか。
      • クラスとそのルーチンをテストする方法について考えたか。
      • 主に安定したインターフェイスと理解しやすい実装という観点から、あるいは主にリソースや速度の観点から、効率について考えたか。
      • 標準ライブラリや他のコードライブラリに再利用可能なルーチンやコンポーネントがないかどうかを調べたか。
      • 参考資料に役に立ちそうなアルゴリズムが載っていないかどうかを調べたか。
      • 詳細な擬似コードを使って各ルーチンを設計したか。
      • 擬似コードを頭の中で検査したか。擬似コードは簡単に理解できたか。
      • 設計作業に戻る必要があるような警告(グローバルデータの使用、別のクラスやルーチンの方が適していると思われる処理など)に注意を払ったか。
      • 擬似コードを正確なコードで表したか。
      • PPPを再帰的に適用して、必要に応じてルーチンをより小さなルーチンに分解したか。条件を作成するときにそれらを文書化したか。
      • 冗長なコメントを削除したか。
      • 最初の作業で満足するのではなく、作業を何度か繰り返して最も良い方法を選択したか。
        コードを隅々まで理解しているか。コードは理解しやすいか。
  • まとめ
    • クラスやルーチンの作成は、反復型のプロセスであることが多い。特定のルーチンを作成している最中に得られた洞察力は、クラスの設計に活かされることが多い。
    • 良い擬似コードを書くには、わかりやすい文章を使い、特定のプログラミング言語に固有の機能を避け、目的のレベルで書く必要がある(目的は、その設計が何を行うのかを説明することであって、それを行う方法を説明することではない)。
    • PPPは、詳細設計のための便利なツールであり、コーディングを容易にする。擬似コードはそのままコメントになるため、正確かつ効果的なコメントが得られる。
    • 最初に思い付いた設計で満足しないこと。コーディングに取りかかる前に、擬似コードを使って複数の方法を試し、最も良い方法を選ぶ。
    • 段階ごとにそこでの成果物を確認し、第三者にも確認してもらう。そうすれば、まだ労力をそれほど注ぎ込んでいない最も安価な段階で、ミスを捕らえることができる。

第10章 変数の使用

  • データリテラシーテスト
    • このような技術書に載せるテストに、敢えての間違いをいれて読み手の真剣さ?を確認させる?のが面白かった。
    • 宣言は変数の型を決定し、定義は変数に特定の値を代入することである。
    • 変数の参照をまとめる最大の利点は、プログラムが読みやすくなる事。
    • 変数の持続間隔、寿命は短い方がよい。
    • 変数のスコープを最小限に抑えることについて
      • プログラマが変数のスコープを最小限に抑える場合は、「便利さ」と「頭での理解しやすさ」をどう捉えるかが決め手になる。
      • 「便利さ」をとるか「頭での理解しやすさ」をとるかは、つまるところ、プログラムを書くこととそれらを読むことのどちらに重きを置くかの違いである。
      • 今までや、プライベートでプログラミングする時は「便利さ」をとっていた事を思い出す。変数はなるべくグローバルにしてたからw
      • でも今は情報隠ぺいやセキュリティーの事もあるが、コードレビューが念頭にあるので変数のスコープはなるだけ狭くを意識できている。
    • バインディングタイム(意識したこともなかった)
      • バインディングタイムとは「変数にその値が結び付けられるタイミングの事」
      • 一般的にバインディングタイムが遅ければ遅いほど柔軟性が増していくと言われている
        • コーディング時(マジックナンバーを使用する)
        • コンパイル時(名前付き定数を使用する)
        • ロード時(WindowsのレジストリファイルやJavaのプロパティファイルといった外部ソースから値を読み取る)
        • オブジェクトのインスタンス生成時(ウィンドウを作成するたびに値を読み取るなど)
        • ジャストインタイム(ウィンドウを描画するたびに値を読み取るなど)
    • まとめ
      • データの初期化はエラーの原因になりやすいので、本章で説明した初期化のテクニックを利用して、予想外の初期値による問題を未然に防ぐ。
      • 変数のスコープは最小限に抑える。変数の参照を近くに集める。変数はルーチンまたはクラスのローカル変数にする。グローバルデータを使用しない。
      • 同じ変数を扱うステートメントが複数ある場合は、それらをできるだけ近くにまとめる。
      • 早期のバインディングは柔軟性を制限するが、複雑さを最小限に抑える。バインディングを先送りすると柔軟性は高まるが、その分複雑さが増す。
      • 変数はそれぞれ1つの目的に使用する。
スポンサーリンク
レスポンシブ