ずばり、この問いに対する自分の考えを得るために、この本を読む。現段階での思いとしては、「オブジェクト指向のこころ」に影響されているため、次の点をその答えとしている。
- 個々のプログラムに責任を委譲( delegete )するため
- 様々な観点で、情報を隠蔽するため
- 流動的要素の抽象的側面と実装を分離するため
すべては、変更されそうなところとそうでないところを整理して、プログラムやシステムのライフサイクルで発生する 本質的に必要な 変更に対してポジティブに向き合っていくための工夫であると考えている。
以降、読んで参考になった箇所について記録する。
プログラミング言語の歴史
昔はハードウェアが高価だったため、ソフトウェアの実行効率を高めることがとても重要だった。すなわち、より少ないステップ数で、より少ないメモリを利用するということであり。可読性や保守性に対する優先度は二の次だった。そもそも、プログラムがこれほどまでに再利用されることを想定していなかった。
例えば、GOTO文など。
OOPとはなにか
構造化プログラミング言語ではできなかったことが2つある。
- 共通サブルーチン以外の再利用
- グローバル変数を使わずに済む仕組み
クラス、ポリモーフィズム、継承をその大きな特徴として挙げている。特にポリモーフィズムの例が秀逸だったので参考に成る。
ポリモーフィズム
構造化プログラミングの産物が 共通サブルーチン だとすると、OOP の産物がポリモーフィズムであり、これは 共通メインルーチン と言える。呼び出される側が増えても、呼び出し側を修正する必要がない。
例外
例外の定義は、 プログラムが仕様として定めている返り値とは異なる形で、エラーを呼び出し側に返す仕組み である。
従来の例外制御は、返り値としてリターンコードを用意し、呼び出し側が判定処理を行うものだった。このやり方だと、判定処理の書き忘れや値の間違いに対してコンパイラが気付けない点が問題となり、プログラマの影響範囲を特定する作業の負荷が高まる。
例外の仕組みを使うことで、サブルーチンが例外を発生させる場合に呼び出し側がきちんと制御している( or throw する)かどうかをコンパイラが指摘してくれるので、効率的である。
OOP になり退化することもある
プログラムの間違いを事前に防止することができることはプログラミングでは重要なメリットである。同じように事前に防止する目的で、あえて 従来は使えたがここでは使えない 機能がある言語もある( たとえば Java。 GOTO文や、共用体、ポインタの直接利用など。)
プログラマが使えないことのデメリットと、プログラミングに与えるメリットとのバランスを考える。
メモリの利用
ガーベジコレクションやメモリの動的確保についての記述があり、オブジェクト指向とどう関連するのか興味がある。
OOPプログラムはメモリの利用に特徴がある
まず、プログラムの一般的なメモリ領域について。
メモリ領域 | 説明 | 情報種類 | 格納単位 |
---|---|---|---|
静的領域 | アプリケーション開始時に確保される | グローバル変数、実行コード | アプリケーションでまとめてひとつ |
ヒープ領域 | 開始時に一定領域が確保され、必要の都度アプリケーションに割り当てられる | 任意(アプリケーションによる) | システムまたはアプリケーションでひとつ |
スタック領域 | LIFO | 呼び出したルーチンの引数、ローカル変数、戻り先 | スレッドごとにひとつ |
クラス情報は、クラスにつきひとつだけ静的領域( メソッドエリア )にロードされる。インスタンスを作る命令が実行されると、そのクラスのインスタンス変数の格納するのに必要な大きさのメモリがヒープ領域に割り当てられる。インスタンスからメソッドエリアにあるクラス情報への対応付けも行われる。インスタンスごとにメソッドがメモリに展開されるわけではなく、インスタンスはクラスごとにロードされたクラス情報にアクセスすることになる。
従来のプログラミング言語で書かれたプログラムでは、グローバル変数やコード情報は静的領域に、サブルーチン呼び出しの情報はスタック領域を使って受け渡すことでほとんどの処理を実現していた。一方 OOP は 有限のメモリ領域であるヒープ領域をじゃぶじゃぶ使って動く 、という点に注意する。
変数には、インスタンスそのものではなくポインタが格納される。インスタンスを格納する変数を他の変数に代入した場合、ポインタがコピーされるだけで、ヒープ領域にあるインスタンスそのものは変化しない。
ポリモーフィズムとメモリ管理
メソッドテーブル がポリモーフィズムを実現する。スーパークラスとサブクラスとでメソッドテーブルの形式を同じにしておけば、どちらのインスタンスに対しても同じ方法でメソッドを呼び出すことができる。
メソッドテーブルには、メソッドがメモリに展開されている場所 ≒ ポインタが格納されている
継承とメモリ管理
メソッドなのか、インスタンス変数なのかによってメモリ配置が全くことなる。特にスーパークラスのインスタンス変数は、ヒープ領域に確保されるすべてのインスタンスにコピーして保持される ことを認識しておく。
ガベージコレクションとは
ガベージコレクション( garbage collection )とは、ヒープ領域に残った不要なインスタンスを自動的に削除する 仕組みである。
この機能は、孤立したインスタンスを自動的に削除する仕組みで、参照先がなかったり、循環参照しているインスタンスがその対象となる。
高い cohesion 、低い coupling
良い設計は、cohesion を強く、coupling を低くすることが重要だとされている。
これは、大きく次の指針を守ることである。
一言で理解可能な名前をつける
クラスの機能を「一言でズバリと表現できる名前をつける」。できないなら、 cohesion が低いと考えられる。
秘密をたくさん作る
クラス間の結合度を低くするために、他のクラスが知らないことをたくさん作る
小さく作る
例えばメソッドなどは、シンプルな目的にとどめた方が良いし、場合によっては1行の定義もありうる。
循環した依存関係をつくらない
実装レベルでいうと、 header file を相互にインクルードしない。依存関係があると、次のようなデメリットが生じる。
-
単独で使えない
-
変更時に影響調査が必要となる
##参考
言語仕様というのは、簡単にいえば、そのプログラミング言語の文法・記号などの意味を厳密に規定したドキュメントのことである。Javaにしろ何にしろ、プログラミング言語には必ずといっていいほど言語仕様が存在している。厳密な規定とは「誰が読んだとしても、同じ意味としてとらえることが可能である」という意味だ。
##まとめ
オブジェクト指向を学ぶためには、オブジェクト指向が取り扱う問題領域のレイヤーをまず理解した上で、自分はどの層を学びたいのかを把握することが重要である。
- 概念(あるコンテキストでは、オブジェクトはどのような責任を持つのか)
- 仕様(オブジェクト同士のメッセージ送受信方法は何か)
- 実装(責任をどうやって果たすか、具体的にどう実現するか)
今回の書籍においては、特に 実装 に関してのオブジェクト指向の特徴を整理することができた。
プログラミングスタイルがオブジェクト指向になり、ポリモーフィズムをメインルーチンの共通化と呼んだり、例外処理を「プログラムが仕様として定めている返り値とは異なる形で、エラーを呼び出し側に返す仕組み」と呼んでいたりと、理解を深める言い換えを学ぶことができた。
また、そこまで考慮しなくても良いと思っていたメモリ管理については、「従来のプログラミング言語とは異なる形で行われること」をプログラマのたしなみとして把握しておくべきだということがわかった。
オブジェクト指向のこころ では、概念 レベルの在り方を解説している。併せて読むとレイヤー間の関連づけに役立てることができた。この本の言わんとしているところについては、冒頭の
- 個々のプログラムに責任を委譲( delegete )するため
- 様々な観点で、情報を隠蔽するため
- 流動的要素の抽象的側面と実装を分離するため
であると理解している。
なお、この本には開発手法( XPなど )の記載もあったが、読書の目的を優先し、割愛することとした。