「オブジェクト指向入門」(第1版)Bertrand Meyer著 の読書記録です。前回はこちら。
2 モジュール性
いいプログラムを書くには「モジュール性を高めなければ」と人は言う。だが「モジュール性が高い」とはどういうことだ?
どんな設計(手法)ならばモジュール性を高くできるのか?それを評価するための基準というものを挙げてみよう。
2.1.1 モジュールの分解しやすさ
複雑で巨大な1つの問題を、単純で小さな複数の問題の組み合わせとして定義し直すことができれば、その問題を理解することが容易になる。
当然プログラムの実装も容易になるし、テストも容易、複数人で開発する場合に作業分担する上でも有効となる。
例えばトップダウン設計によってモジュールを分解することができる。最上位にいるシステムの利用者は、それは実社会での役に立つのだろうかという抽象的な目でシステムを捉える。どうやって実現されるかは関心の外だ。その下で、システムを構成する各モジュールも同じように与えられた仕事をさらに下位のモジュールへ分解していく。これによって誰も(利用者もモジュールも)が、自ら責任を引き受けるべき仕事にのみ集中できるようになる。
モジュール同士の依存性が大きくなるような設計手法を採用していると、この基準は満たせなくなる。例えば、あらゆる初期化処理を特定のモジュールで行なっていた場合、そのモジュールはプログラムの全体と依存することになり「これを直すのがどれだけ難しいか、私には判断できないよ」という状況が発生しやすくなる。
2.1.2 モジュールの組み合わせやすさ
あるモジュールができるだけ多くの局面で利用できればうれしい。
例えば数学ライブラリは様々な場合に利用できる。Unix系OSのシェルで動作するプログラムは、標準出力などを介して他のプログラムと組み合わせて利用可能になっている。
他の例を自分なりに考えてみると・・・
- たいていの言語に用意されているソート機能は、要素同士の比較を行う手続きを引数として受け取れる。これで、これまでになかった画期的なオブジェクトをあなたが発明した場合でも、比較の仕方さえ定義されていれば既存のソートライブラリでソート可能となる。
- データを読み込んで何らかの処理をする関数を作ろうといった場合、引数にファイルパスを受け取るようにしてしまったらその関数はファイルに対してしか使えない。ストリームを受け取るようにすれば、ファイルでもメモリ内のデータでも、どんなデータに対しても使える。
- 連想配列とかハッシュとかディクショナリとか呼ばれる(任意のキーから値を参照できる)ライブラリは、equalsやhashcodeが適切に定義されてさえいればどんなオブジェクトでもキーとして利用できる。
2.1.3 モジュールのわかりやすさ
モジュールを1つだけ取り出して、それを人が見たときにわかりやすいかどうか?
保守のコストを低減するためにも、わかりやすい方がいいに決まっている。しかしどうすればわかりやすくなるだろう?
ここでも出てくるのは他のモジュールとの依存性を小さくすべき、という原則だ。依存性が小さければ、そのモジュールだけをみて理解するといったことが容易になる。
例えばグローバル変数を使うことでモジュールはわかりにくくなることが多い。その変数はどこか別のモジュールで変更されるかもしれない。または別のモジュールで初期化をしてもらわないと、そのモジュールは正しく動作しないかもしれない。いずれにしても、そのモジュールについて理解するためには、他のモジュールについても知っておかなければいけなくなる。
2.1.4 モジュールの連続性
あるモジュールを変更した場合、その影響が他のモジュールに及ばないことを連続性という。
例えば、実装とインターフェースを分けることがこれにあたる。あるプロパティはメンバ変数として定義されていたのでそのgetメソッドは単にその変数値を返すだけだった・・・が、あるときそれは実行時に計算しないと出すことのできない値ということになってしまった。
そんな場合でも、そのモジュールを常にgetメソッド越しに利用しているならば、その実装が変更されたとしても外部のモジュールは修正して回らなくてすむ。実装が変更されたことに気づく必要すらない。
2.1.5 モジュールの保護性
あるモジュールの中で異常が発生した場合の影響を限定できるかどうか。「ここでエラーになったらその影響が及ぶのはここまで」ということを定義できるか。
異常が発生した場合に気を遣わなければいけないのは、「中途半端に壊れた状態のまま物事が進んでしまうのを避ける」ということだ。DBMSの世界で必ず学ぶトランザクションを考えればいい。ある処理は成功するか失敗するかの2通りの結果のいずれかのみを生じるようにし、失敗したなら処理を試みる前の状態に戻すべきだ。
それを怠ると、どこか思いも寄らない場所でまた別の異常を発生させてしまう可能性を残すことになり、影響範囲の限定が不可能となってしまう。