アルパカログ

プログラミングとマネジメントがメインです。時々エモいのも書きます。

続々・CODE COMPLETE 〜美しいコードを書くために〜

前回は主に防御的プログラミング、変数、例外について書いた。

今回は主に数値、浮動小数点数、文字列などプリミティブなデータを扱う際の注意点とステートメントについてまとめる。

ほとんどの問題に対しては、(再帰は)複雑きわまりない解決策になるだろう。

数値全般

  • マジックナンバーは使わない
  • 必要であれば0や1をハードコーディングする
    • 0と1の値は、インクリメント、デクリメント、および配列の先頭からループを開始するために使用する
  • 0による除算を考慮する

整数

  • 整数の除算に注意する
    • 7/10 は0.7ではない
    • 通常は0か、負の無限大か、最も近い整数になる
  • 桁あふれに注意する

浮動小数点数

  • 小数部の多くを正確に表現できないことに注意する
    • 循環小数は通常、7桁または15桁の精度でしか表せない
  • 大きさが極端に異なる数の加算や減算は行わない
  • 等価を比較しない

文字と文字列

  • マジックストリングを使わない
    • "Gigamatic Accounting Program" など
    • 代わりに名前付き定数を使用する

ステートメント

  • 複数のステートメントを特定の順序で並べなければいけない場合は、その依存性を明白にする

悪い例

ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary
  • 依存性が隠されてしまっている
  • 関数呼び出しには引数が1つもないので、それぞれがメンバ変数にアクセスすることは察しがつく
    • が、これを読んだだけで確信することはできない

良い例

expenseData = InitializeExpenseData( expenseData )
expenseData = ComputeMarketingExpense( expenseData )
...
expenseData = ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )
  • 引数を使って依存性を明白にする
    • 関数どうしがデータをやりとりするように書き直せば、実行順序が重要であるという手がかりになる
  • 依存性が明白でない場合はコメントに書く
  • コードをストレートなものにする原則として最も重視されるのは、実行順序への依存性である

ループの制御

  • ループの前処理や後処理は、ループの先頭または末尾にまとめる
  • ループの1回の処理は1つの機能に絞る
    • それぞれが1つのことだけに集中するという点においては関数と同じ
  • for ループを終了させるためにループ変数を勝手に書き換えない
  • ループ変数の最終値を使用するコードを書かない
  • ネストしたループが読みやすくなるように変数に意味のある名前を付ける
    • ループ変数のクロストークを防ぐ
    • ij を取り違えるようなことをループ変数のクロストーク(混線)という

その他

  • 論理式の評価には true と false を使う
    • 0や1という数値を使わない
  • true, false は暗黙に照合する
    • done == false のようにしない
  • 複雑な式はブール関数(評価関数)として独立させる
  • 肯定的な論理式にする
    • ほとんどの人は否定文を繰り返されると理解するのに苦しむ

まったくのまぬけでなくもないのだ。

  • ド・モルガンの定理を適用して否定語のある論理評価を単純化する
  • かっこを使って論理式を明確にする
    • 読む人が、言語が論理式を評価する順番を理解しているとは限らない
    • かっこを使うとコードを読む人の負担が軽くなる

かっこが少なすぎる例

if ( a < b == c == d )
  • 論理式が評価される方法を知る
  • 数値を含んでいる式は数直線の順番に並べる
    • 要素を左から右へ、小さい順に並べる

MIN_ELEMENTS <= i and i <= MAX_ELEMENTS
  • 3レベル以上のネストを理解できる人はほとんどいない
    • 条件文を再評価して、ネストしたif文を単純化する
    • 関数として独立させる

ガード句

  • ガード句を使って複雑なエラー処理を単純化する

再帰

  • 再帰は状況を選んで使用する
  • 再帰は、ほとんどの問題に対して複雑きわまりない解決策になる
  • 再帰を使って行えることはすべてスタックと繰り返しを使って実現できる

おわりに

3本の記事に渡ってCODE COMPLETE上巻の内容をまとめた。

ここまでの内容を頭に入れておくだけでも、コードの品質はぐんと上がりそうだ。特に、継承を濫用するとプログラムの複雑さを増してしまうということに留意しておきたい。

下巻はリファクタリングやテストについて書かれているので、読んだらまとめていきたい。