自分の理解を深めるために投稿します。
第7章:高品質なルーチン
ルーチンを作成する理由
コードの重複を避けるためだけではない
- 複雑さの低減 ← 設計概念で大事なやつ!
- 中間部分をわかりやすく抽象化する
- コードの重複を避ける
- サブクラスを作成しやすくする
- ルーチンが十分に分解され、短く保たれている必要がある
- 処理順序を隠蔽する
- ポインタの処理を隠蔽する
- エラー原因になりやすい部分をまとめる
- 移植性を向上させる
- 複雑な論理評価を単純にする
- 片付け
- 評価の目的がわかりやすい関数名になる - パフォーマンス向上
- 1箇所で最適化できる
小さいルーチンでも作成しよう
/// ルーチンを使わない場合
points = deviceUnits * ( POINTS_PER_INCH / DeviceUnitsPerInch() )
/// ルーチンを作成した場合
Function DeviceUnitsToPoints ( deviceUnits Integer ): Integer
DeviceUnitsToPoints = deviceUnits *
( POINTS_PER_INCH / DeviceUnitsPerINch() )
End Function
points = DeviceUnitsToPoints( dviceUnits )
全てのルーチンが小さくないといけないわけではない
1つの大きなルーチンで処理したほうがいい時もある
良いルーチンとは
-
最高!
- 機能的凝集度
-
理想的でない
- 情報的凝集度:決まった順序で実行されなければならない処理で構成されている
- 連絡的凝集度:同じデータを利用するがそれ以外に関連のない処理で構成されている
- 時間的凝集度:同時に実行される複数の処理でまとめられている
-
容認できない
- 手順的凝集度:処理が特定の順序で書かれている(特定の順序でないとエラーが起きるわけではない)
- 論理的凝集度:渡されたフラグによって複数の処理のうち1つが実行される
- 例外:一連のif/case文と他のルーチンの呼び出しのみで構成されているなら良い(イベントハンドラ)
- 暗号的凝集度:ルーチンの処理同士にそれと分かる関連がない
良いルーチン名とは
- ルーチンが行うことを全て説明する
- 曖昧な動詞を使わない
- 必要な長さのルーチン名にする
- 関数名には戻り値の説明を反映させる
cos();
customerId.Next();
- 機能的凝集度の高いルーチンを作成できているのなら、プロシージャ名は
「動詞 + オブジェクト名」 で事足りる - ただし、オブジェクト指向言語の場合はオブジェクト自体が呼び出しに含まれるので、オブジェクト名を含まなくて良い
// 動詞+オブジェクト名
PrintDocument();
// 動詞のみ(オブジェクト指向言語で書いている場合)
document.Print();
- 正確な反意語を使う
add/removeincrement/decrementopen/closebegin/end... - 命名規則を決めておく
良いルーチンの長さとは
- ルーチンの長さとエラーの発生率に深い関係はない
- むしろ凝集度、ネストの深さ、変数の数、分岐の数、などといった複雑さに関する問題によって決定する
- ただし、200行以上のルーチンを書く場合は注意が必要
- わかりやすさの上限が200行
ルーチンの引数
- 入力、変更、出力の順だとわかりやすい
- そうでなくても、引数の順序に一貫性があるほうが良い
- 複数のルーチンが似たような引数を持つ場合は、順番を統一する
- 全ての引数を使用する
- 状態変数やエラー変数は最後に配置する
- 引数に関するインターフェイスの条件を明記する
- 入力専用?変更?出力専用?
- 単位
- 期待される値の範囲 など
- ルーチンの引数はだいたい7個まで
- 引数の渡し方は抽象化を元に考える
- 別々に渡すか
- オブジェクトごと渡すか