MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

プログラマの聖書『CODE COMPLETE 第2版 上下巻』

推薦文

これまで読んだソフトウェアコンストラクションの書籍の中で

最高の一冊である。

どの開発者も同書を一冊手に入れ、毎年最初から最後まで読むべきだ。

私は9年間これを続けているが、それでもまだこの本から学ぶことがある

"John Robbins"


自分の趣味として、

本当にこの人すごいなという人には、

「オススメの本ってあります?」

と聞くようにしています。


すると、プログラミングがすごい人から、

最も薦められる本が、

『CODE COMPLETE 第2版 上・下』です。


ある人は、

「プログラミングを仕事にするなら、読んでいたほうがいいよ」

といい、ある人は、

「これの本を読んでいない人は、プログラマと名乗ってはいけないね」

といい、ある人にいたっては、

「この本を知らない人とは、仕事をしないようにしているんだ」

と言っていました。


プログラミングを仕事の大部分としている自分ですが、

これまで上下巻セットで1万円以上するため、

ちょっと尻込みしていました。

しかし、とある理由で一万円の図書カードをゲットしたので、

ずっと欲しかった本書を手に入れることができました。


これから読むので、

正直、この本を推薦する資格はないと思いますが、

この本の名声だけで名著だということは明らかです。

下記は自分用のメモですが、

それを読んで少しでも興味が出た人は、

購入してみることをオススメします。


もし、プログラマとして

ちょっとでもレベルアップすることができたら、

一万円などすぐにペイできますし、

読むのが早ければ、早いほど、

その確率は増大すると思います。

第1章 ソフトウェアコンストラクションへようこそ

本書を執筆する上で私が最も配慮したのは、

業界の第一人者や教授らの知識と

一般的な商用プラクティスとの差を縮めることである。


コンストラクションの殆どの部分は

コーディングとデバックだが、

詳細設計、コンストラクション計画、

単体テスト、統合、統合テストなどの

アクティビティも含まれる。


コンストラクションは

一般にプロジェクト全体の30%~80%の時間を占める。


テストや要求定義は無視されることがあるが、

コンストラクションは必ず行われるため、

コンストラクションは重要


コンストラクションにおける、

個々のプログラマの生産性には10倍から20倍もの開きがある。

第2章 ソフトウェア開発への理解を深めるメタファ


プログラミングは建築に似ている


欠陥が紛れ込んでから、発見されるまでの時間が長ければ長いほど、

欠陥の修正コストは跳ね上がる。


単純で、要求があまり変わらないものには、

事前準備をちゃんとする。

それ以外は作業を進めながら、準備しても良い。

第3章 2回測って、一度で切る: 上流工程の必要性


要求分析はユーザの立場で、言葉で語られるべき。

欠陥が紛れ込んでから、発見されるまでの時間が長くなればなるほど、欠陥の修正コストは跳ね上がる。

 

課題定義は、ソリューションとして考えられるものに、一切言及せずに、

課題が何であるかを定義することである。

 

各構成単位が他の構成単位について知っていることをできるだけ減らすのが、情報隠蔽

 

国際化はi18nと表記されることが多い。

 

プログラムコードの90%は例外処理や後処理のために

書かれたものである。

 

第4章 コンストラクションも重要な決断

Pythonはc言語の6倍、コード一行での命令の数が多い


COBOLは英語に似せた言語


SQLはStructure Query languageの略

第5章 コンストラクションにおける設計

私達ソフトウェア開発者はプログラム全体を一気に頭に詰め込むべきではない。


プログラムの部分ごとに集中できるように、

プログラムを整理して、一度に検討するプログラムの量は、

最小限に留めることを目指すべきである。


すべてのソフトウェア設計手法の目標は、複雑な問題を単純な問題に分割することである。


Voltaireによれば、本が完成するのは、他に追加できるものがなくなった時ではなく、

これ以上削除するものが無くなった時である。


GUIやDBにアクセスするソフトは別のバッケージにする


良いクラスインターフェースは氷山の一角のようなもので、

クラスの殆どの部分を公開しない。


情報隠蔽には二種類ある

1.複雑さを隠蔽して、必要がなれば考えずにすむようにする

2.変更の源を隠蔽して、変更した時にその影響を一箇所に留める。

情報隠蔽を行うと6倍コード変更が容易になる。


何を隠蔽すべきかと自問する習慣をつけよう

クラスにgetterとsetterをつけることにより、

その関数をsynchronizedすることで、

クラスへのアクセスをスレッドセーフにすることができる。


ソフトウェアでも、モジュール間結合はできるだけ単純にしよう。

密な結合の場合
・ルーチンの引数が多すぎる
・同じグローバルデータを使用する2つのクラス


プロトタイプは、それを捨てるつもりで書く。最終系と違う言語で書くのも良い。


良い設計は反復的である。


何を隠蔽すべきかを問う。


設計に関する話し合いの内容はすべてWikiに書く。

図は、ホワイトボードや手書きの図の写真で良い。

第6章 クラスの作成

クラスとは強くて明確な責務を

共有するデータとルーチンの集まりである。


コードのある部分を作業している時に、

無視しても問題ない部分をできるだけ

増やすことが優秀なプログラマになるための鍵である。

クラスはその目標を達成するための第一のツールである。


クラスはADT+継承およびポリモーフィズム

として考えることができる。

抽象化とは、複雑な処理を単純に見せる能力である。


クラスのパブリックインターフェースは、

レイヤーを合わせるようにする。


クラスの使い方を知る為に、

クラスの実装(private)を見始めていたら、

インターフェースの設計が間違えている。


使い方がわからないと言われた場合は、

口頭で教えるのではなく、

インターフェースを修正して、渡す。


クラスのメンバ変数が7個を越えたら、クラスを分割することを考える


インスタンスが一つしか無いクラスは、クラスである必要は無い。


シャローコピーを使う理由がなければ、できるだけディープコピーを使う。

パフォーマンスよりも、バグを入れることによるコストの方が大きいことが多い。


クラスを作成する最も重要な理由は、プログラムの複雑さを低減することである。

情報を隠蔽するクラスを作成して、その情報について考えずに済むようにしよう。

第7章 高品質なルーチン

ルーチンを作る理由

1. プログラムの複雑さを和らげること。

2. プログラムの重複を避ける

3. 処理順序を隠蔽する。


入力専用のすべての引数にはconstを使う。


ルーチンに渡した引数はすべて使う


状態変数、エラー変数は引数の最後に置く


引数の数は最大7個にする。

それ以上になった時はデータをクラス化する。


関数:値を返すルーチン

プロシージャ:値を返さないルーチン

ルーチンの名前の付け方「動詞+オブジェクト」

ルーチンのcohesionが強い -> ルーチンが一つのことだけをする。

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

よいプログラムは、何を入れてもゴミを出さない。


アサーションとは、開発時にプログラムの実行をプログラム自体で検査するコードのことである。

通常はアサーションコードは

開発のコードではコンパイルし、

製品版のコードではコンパイルしない。

製品版ではパフォーマンスを落とさないためである。


発生が予想される状況にはエラー処理コードを使用し、

発生してはいけない状況にはアサーションを使用する。


アサーション内にコードを入れては行けない。bool変数のみ入れる。


最適なエラー処理はエラーが発生したソフトウェアの種類による。


例外はエラーや例外イベントを呼び出し元のコードに

渡すことができる特別な手段である。

基本的には、お手上げだとあきらめて、

どう対処すればよいかわからない、誰か対処方法を知らないか?と叫んでいるようなものである。


例外はアサーションと同じような状況で使用される。

絶対に発生してはいけないイベントで使用される。

言語機能として例外処理があるからといって、必ず使う必要はない。

防御的プログラミングコードは多すぎても、少なすぎてもいけない。

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

擬似プログラミングプロセス
(Pseudocode Programming Process:PPP)

擬似コードはそのままソースコードにコメントとして残す。

擬似コードは普通の日本語で書く

言語に依存しないように擬似コードを書く

だれかがすでに博士号の論文を書いているようなもので時間を無駄にするのはやめて、

既に書かれているコードを数分調べて無駄な作業を未然に防ぐ。


第10章 変数の使用

不適切なデータの初期化は、コンピュータプログラミングのエラーの温床である。


変数は宣言時に初期化する。


変数は最初に使用する場所の近くで初期化する。


できるだけconstを使用する。


クラスのメンバデータはすべてコンストラクタで初期化する。


名前空間はクラスのコレクションである。


変数の寿命はできるだけ短くする


ループで使用する変数は、ループの直前で初期化する。


グローバル変数を使うか、ローカル変数を使うかは、

便利さをとるか、頭での理解のしやすさを取るかの問題になる。


C++のstatic関数はプログラムが終了するまでメモリ上に残る。


一つの変数は、一つの目的以外には使わない。


隠れた意味を持つ変数は使わない。

例) 変数pageCountは印刷するページを表すが、-1の場合はエラーを表す。

エラーは別のステータス変数で表す。


第11章 変数名の力

変数に名前を付ける時に最も重要なことは、

変数が表すものを完全かつ正確に説明するような名前を付けることである。


フラグにhogehogeflagという名前をつけると、

内容が伝わらないので、もっと明確な名前にする。


bool変数には,doneやerror,found,sucess,okなどの名前を付ける。


省略のガイドラインとして、

一文字目以外のすべての母音を削除するという方法がある。

例) computer -> cmptr, screen -> scrn


プロジェクトで、省略を使用する単語のリストを作って

バージョン管理する。


カンマとピリオドを間違えたことで宇宙船がダメになったことがある。

なので間違えやすい名前は使わない。


第12章 基本的なデータ型

マジックナンバーは使わない

0,1以外は名前付き変数に置き換える。

整数の計算では中間結果の桁あふれに注意する

floatは=で比較しない。

Cの文字列はできるだけ使わない。

C++ではUnicode用のライブラリが必要になる

bool変数を使って、複雑な論理式をわかりやすくする。

列挙型を使うと、複数のif文の条件式を数字にしなくて良くなる。

配列を使いたい場合はSTLを使う。

単純なデータ型を作りたくなった時は、クラスを使うことでより良くならないかを考える。

第13章 特殊なデータ型

本章で述べることは、

最近のオブジェクト指向に沿って

コーディングすれば必要ないことが多い


構造体を使用するメリット:

ルーチンの引数リストを単純にすることができる。

しかし、ルーチンには必要なデータのみを

渡すようにする。


ポインタは最もエラーを引き起こしやすい分野の一つである。

32ビットプロセッサのアドレスは、

0x0001EA40のような32ビットの値である。


できれば、ポインタ以外の技術を使用する。


参照渡しの引数にはポインタを

値渡しの引数にはconst参照を使用する


・グローバル変数を使用してはいけない理由


グローバル変数と同じ名前のローカル変数を

関数内で使用するとおかしなことになる。


複数のスレッドでグローバル変数を

使用するのは難しい


グローバル変数は初期化の手順が一定ではない。


グローバルデータ使用する時も

できるだけアクセスルーチンを作ることで、

クラス化しやすくなる。

第14章 ストレートなコードの構成

コードの順序が重要な場合は、

重要であることがわかるようなコードにする。


関数名にinitなど依存関係が

わかりやすいようにする。


変数の寿命をできるだけ短くするようにすると

読みやすいグループ化されたコードになる。

第15章 条件文の使用

if文では正常なケースを最初に書き、else文をエラー処理にする。


else文に何もコードが必要ない場合は、その旨をコメントで記入する


if文の条件文が複雑な場合は、is関数を作って簡約化する


case文は、アルファベット順か頻度の高い順にする。


case文の中は単純にする


defaultはエラー処理に使う


C++のcaseにはbreakが必要

第16章 ループの制御

様々な種類ループをいつどのように、使用するかを知ることは、

高品質なソフトウェアを決定する要素の一つだ。


一回は必ず実施したいルーチンがある場合、

ループの前に実施するのではなく、break文を使用する。


終了条件は一箇所にまとめる。


途中でループを抜ける場合は、whileループを使用する。


forループは単純なものに使用し、

複雑なものにはwhileループを使用すること


あるファイルを最後まで読むのであれば、

EndFile関数を使って、while文にする。


forループを終了させるために、ループ変数を勝手に書き換えない。


ループの終了条件を厳密に制御したい場合は、whileループを使用する。


ループ変数の最終値を使用するコードは書かない


whileループの終了条件はブール変数を使用するのではなく、

break文を使用する。


ループをbreak文だらけにしない。


continue文はループの先頭で使用する。


ループ変数には意味のある名前をつける


ループ変数のスコープはループ内に留める。


一つのループを1画面より長くしない


ネストは三段階に制限する


ループを作成する時は、簡単な部分から徐々に作成する。

第17章 特殊な制御構造

複数のエラーを処理したい時は、

ルーチンの最初にreturn文を繋げる

ただ、無駄にreturnを増やさない


再帰とは、問題を細分化して、

ルーチンでその一部分を解決し、

自分自身を呼び出しながら、

他の部分を解決していくという手法である。


再帰を使用する場合は

かならず再帰が終了するようにする

そもそも再帰を使用する前に別の手段を検討する

第18章 テーブル駆動方式

テーブル駆動方式とは、

ifやcaseで情報を検索するのではなく、

テーブルの情報を参照するしくみであり、

ロジックが複雑になると有効になる。


テーブル駆動の場合は、

データを読み込むような形にすると

テーブルを変更しやすくなる。

第19章 制御構造の問題

論旨力の真と偽には,0や1ではなく、

trueとfalseを使う


複雑な条件式の場合はブール変数で整理(文書化)する。


条件式で否定はできるだけ使わない。


数値の評価は数直線と同じ並びにする


ネストが深い場合は、それぞれに関数を作るか、

ファクトリメソッドでそれぞれのオブジェクトを作るようにする。


構造化プログラミングは、

プログラムの入口と出口はそれぞれの一つであるべきという考え方であり、

continue,return,break throw-catchの使用を批判的に見る

第20章 ソフトウェアの品質

相反する目的の中から

最適のソリューションを見つけることによって

ソフトウェア開発は初めてエンジニアリングになる


ソフトウェアのテストを実施しても、

バグの検出率は30-35%しかならない。


検出率を向上させたい場合は、

様々な手法を組み合わせる必要がある

(テストのみではダメ)

1. 公式なインスペクション

2. モデリング・プロトタイピング

3. コールドリーディング

4. テスト


開発スケジュールを短縮する最も良い方法は、

ソフトウェアの品質を向上させ、

ソフトウェアのデバックや

作業のやり直しにかかる時間を減らすことである。


バグの無いコードを書くには、

バグのあるコードを書くよりも、

時間がかかるわけではない。


参考資料

myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com
myenigma.hatenablog.com

MyEnigma Supporters

もしこの記事が参考になり、

ブログをサポートしたいと思われた方は、

こちらからよろしくお願いします。

myenigma.hatenablog.com