#諸言
学習履歴全体のリンク
https://qiita.com/shigural/private/444b991293e5ab1b40af
#本文
##CODE COMPLETE(第2版)
###1.基礎を固める
システムの設計・テストなどを行う(コンストラクション)は作業全体の30~80%を占める
###2.ソフトウェア開発への理解を深めるメタファ
ソフトウェア開発を行う際には十分な準備が必要である
###3.2回測って、1度で切る:上流工程の必要性
設計・プロジェクト計画といった上流工程を行うことにより、プロジェクトが最も上手く進行することはデータにより示されている。
###4.コンストラクションの重要な決断
各種プログラミング言語について、C言語に対し1行あたりの命令の数は次のようになっている。
C:1
C++:2.5
FORTRAN 95:2
Java:2.5
Perl:6
Python:6
Smalltalk:6
Visual Basic:4.5
###5.コンストラクションにおける設計
システム設計は、1つが完成することで初めて問題や注意点がわかるため、反復して何度も行うべきである
また、決めた設計でも一般的にコードを書いているうちに設計変更することが数回ある
良い設計として、次のようなものが挙げられる
・複雑化を減らす
・保守性
・疎結合
・拡張性
・再利用性
・高いファンイン(あるクラスを使用するクラスがたくさんある)
・低いファンアウト(1つのクラスが使用するクラスの数が少ない。特に7つ以上は高いファンアウトである)
・移植性
・無駄の無さ
・階層化(その層のみを見れば実装がわかる)
・標準化手法(ex:コーディング規約などによる一貫性)
設計を行った後、それを1週間後に見返すことでよいレビューができるだろう
###6.クラスの設計
抽象データ型(ADT)はデータと、データに対する操作をまとめたものである。
継承を行う際にはis-a関係を意識し、これを満たさない場合には継承してはならない。
インターフェイスは意味的なものではなくプログラム的なものにする必要がある。たとえば、順次実行しなければならない複数のメンバ関数を作ることは良くない。
クラスのデータをまとめたクラスを所有させることで、実装の詳細をよりカプセル化することができる。
データはprotectedやpublicにせず、常にprivateにするとよい
1つのクラスにはデータを5~9以内に抑えるべきである。
継承のために設計したクラス以外を継承してはならない。継承不可能なようにfinalをつけるなどすると良い
派生クラスが1つしかない基底クラスは作る必要がない。将来のことを考えたプログラミングより、これに関しては今のみを見たほうが良い
オーバーライドして、何もしない関数を作る設計は間違っている。is-aではなくhas-aにするべきである
継承を深くしすぎることは防ぐべきである。多くとも6段階であるが、基本的には3段階以内にするほうが良い
case文が多くなる実装は、仮想関数による関数呼び出しに置き換えるほうが良い
多重継承は使ってもよいが、十分検討するべきである。
コンストラクタにおいて、すべてのメンバデータは初期化されるべきである。
また、Singletonを利用する際にはプライベートコンストラクタを利用することでSingletonであることを強調するべきである。
一般に、シャローコピー(浅いコピー)よりディープコピーを優先するべきである
振る舞いだけを持ち、データを持たないクラス、またはその逆は望ましくないクラスである。
###7.高品質なルーチン
ルーチンにすることは、そのプログラムの単純さとは関係がない。つまりたった1行のコードでも必要を感じればルーチンにして良い
ルーチンにするための正当な理由は次が挙げられる
・複雑さを低減する
・中間部分をわかりやすく抽象化する
。コードの重複を避ける
・サブクラスを作成しやすくする
・処理順序を隠蔽する
・ポインタの処理を隠蔽する
・移植性を向上させる
・複雑な論理評価を単純にする
・パフォーマンスを向上させる
また、次の項目はクラスを作成する理由であり、ルーチンを作成する理由でもある
・複雑さを分離する
・実装の詳細を隠蔽する
・変更による影響を固定する
・グローバルデータを隠蔽する
・制御を一元化する
・コードの再利用を促進する
・特定のリファルタリングを実行する
ルーチンには凝縮度を上げることが求められる。
-理想的なルーチン-
【機能的凝集度】
ルーチンが処理を1つだけ行う。sin/GetCustomerNameなど
-理想的でないルーチン-
【情報的凝集度】
ルーチンの順序を固定する。
しかし、一般的には理想的でない。順番を指定して実行しなければならないルーチンの代わりに、数値を返すルーチンを利用する
【逐次的凝集度】
関係あるデータを使って、かつ順番に意味がある凝集度。
凝集度はかなり高めだが理想的とはいえない
【通信的凝集度(情報的、順次的凝集度)】
ルーチン内の処理が同じデータを使用する。凝集度は高めであるが理想的とはいえない
【手順的凝集度】
機能の実行順序を取りまとめる
【時間的凝集度】
凝集度のレベルとしては高くないが、効果的な場合もある
【論理的凝集度】
似た機能が集まっているが、とくに凝集していない。一般的には避けるべきである。
しかし、ルーチンの目的がコマンド送信であり、それ自体が処理を行わない(イベントハンドラ)なら良い設計である。
【暗号的凝集度】
特に凝集している理由がない。避けるべきである。
ルーチンは動作を1つに抑え、それを説明する適切な名前をつけると良い。なお、ルーチン名を数字だけで区別するのは一般に良くない。
ルーチンを構成する際には、長さで制限をかけるのではなく複雑さで考えるとよい。ただし、ルーチン内部の行数が200を超えるとそれは良くない設計の可能性が極めて高い。
ルーチンに与える引数は、入力、変更、出力の順にするとよい。
与えた引数は必ず、すべて利用し、またルーチンの引数を作業用変数として使用してはならない。
状態変数・エラー変数は出力の一部なので最後に配置することが適切である。
引数に条件がある場合、コメントで描くよりもアサーションを利用して書くと良い。
引数の数は7つに制限するべきである。何種類ものルーチンに同じデータを渡している場合、それらを1つのクラスにまとめてクラスデータとして扱うべきである。
インライン化はカプセル化に違反する。そのため、十分に高価が見込まれない場合は利用しないほうが良い。
###8.防御的プログラミング
####8.1 無効な入力への防御
すべてのルーチンで、正常な入力値の範囲と不正値への処理方法を決めておく必要がある
####8.2 アサーション
発生が予想される状況にはエラー処理高度を利用し、絶対に発生してはならない状況にはアサーションを使用する。
なお、アサーションの引数には関数を利用しないようにすることで、安全性を確保する。
//悪い例
Assert(PerformAction());
//良い例
actionPerformed=PerformAction();
Assert(actionPerformed);
アサーションの出力として、事前条件と事後条件(処理終了時に確約される特性)を含めるべきである
####8.6 デバッグエイド
♯if DEBUG>LEVEL_A
のようにしてデバッグは最後に削除できるようにしておくとよい
###9.擬似コードによるプログラミング
####9.1 クラスとルーチンの作成手順の概要
クラスは次の手順で作成する
1.クラス全体を設計する
2.クラスの各ルーチンを作成する
3.クラス全体をレビューし、テストする
####9.2 擬似コードプログラミングプロセス(PPP)
・特定の処理を性格に説明する文章を使用する
・プログラミング言語の公文要素を利用しない
・目的のレベルで擬似コードを書く(実装方法ではなく何をしたいのか)
・ほぼそのままコーディングできるくらいの詳細sレベルで擬似コードを書く
良い擬似コードの例
if 別のリソースが利用できる
ダイアログボックス構造体を割り当てる
if ダイアログボックス構造体の割当が可能である
使用するリソースが1つ増えることを記録する
リソースを初期化する
呼び出し元から提供された場所にリソースの数を格納する
Endif
Endif
新しいリソースが作成された場合はtrueを返し、そうでない場合はfalseを返す
PPPを利用する場合には、擬似コードをコメント分として設計を行う
####9.3 PPPを使ったルーチンの作成
・複数の擬似コードを作成し、その中で最も良いものを選ぶ
・擬似コードをコメント的に直しつつ(例えば「if エラーコードが有効」→「エラーコードが有効」)コードを記述する
・必要ならばコメントを書きたす
---ここ以降の内容はほぼ、Effective C++とリーダブルコードで十分書かれているように思われる---
###10 変数の使用
###11 変数名の力
###12 基本的なデータ型
###13 特殊なデータ型
###14 ストレートなコードの構成
順序が大切なステートメントを作る場合、その順でなければ実行できないようにするべきである
###15 条件文の使用
###16 ループの制御
###17 特殊な制御構造
###18 テーブル駆動方式
###19 制御構造の問題
###20 ソフトウェアの品質
下巻:略