オブジェクト指向の「オブジェクト」とは「object型」のことではない私のブログを読んでくださっている方なら、オブジェクト指向言語には慣れ親しんでいるかと思います。でも、「オブジェクト指向」をちゃんと説明できますか?
「オブジェクト指向」の疑問
オブジェクト指向の初学者は、まず最初にオブジェクト指向の3要素を学ぶと思います。- カプセル化
- 継承
- 多相性(ポリモーフィズム)
復習しておくと、カプセル化は内部の動作手順を隠蔽し、許可された手法のみを使って操作をできるようにすること、継承はクラス内容を引き継ぎつつ追加の機能を付けたすこと、多相性はメソッドを呼んだ時のふるまい等を実行時の型に合わせて決定することですね。
C++やJava、C#などの代表的なオブジェクト指向言語では、このような要素に対して「クラス」「継承」「メソッドのオーバーライド」などといった具体的な機能が備わっており、これらの要素と言語機能の習得を並行して行うと理解がよく深まるかと思います。
ですが、オブジェクト指向に関する勉強がこれで終わっていないでしょうか。
これはオブジェクト指向の特徴を挙げただけであり、そもそもなぜ「オブジェクト指向」という名前かということには何も触れていません。
そして、そのままオブジェクト指向言語を習得していくと、
- ありとあらゆるクラス・型の最上位にはobject型がある(すべてのクラス・型はobject型を継承している)
- ありとあらゆるメソッドは何かしらのクラス(=object型を継承したもの)に属さなければならず、オブジェクトへの操作として定義される
そもそも、上の箇条書きで出てきた「オブジェクト」って何ですか?クラスのこと?それともインスタンスのことでしょうか。なんとなく意味も分からず、クラスやインスタンスとの違いも明確にしないまま適当に横文字をしゃべっているように見えません?かといって、objectを辞書で引くと「モノ」という訳が出てくるだけで、そんな抽象的な名詞では何も理解が進むことはありません。
だいたい、「オブジェクト言語」ではなく「オブジェクト指向言語」と呼ばれるのもなぜなのでしょうか。上の箇条書き程度の理解では、全然「指向」の意味がわかりませんよね。「指向」すなわち「oriented」を辞書で引くと「~の方向を向いている」とか「~を中心に考える」と言った訳が出てきますが、「オブジェクト」が何かよくわからないのに、そっちを向いているとかそれが中心だとか言われてもわけわかりません。
モジュール化
さて、「オブジェクト指向」とは何かについて紐解いていく前に、ここで一旦「モジュール化」の話をしましょう。コンピューターが生まれたばかりの頃はごく小規模なプログラムが動いていましたが、コンピューターが発達するにつれて非常に大規模で複雑なプログラムが必要になってきました。
ですが、各プログラムの部分が複雑に絡み合っていては全貌が見えにくくなり、また、複数人での分担してプログラムを書くこともしにくくなります。当然、管理も難しくなって、機能の変更や追加があった時や不具合があった時に太刀打ちできなくなってしまいます。
なので、プログラムを何らかの基準で分割し、整理する必要が出てくるのです。こういうのを一般にモジュール化と言います。
ではどこで分割するのが良いでしょうか。
デイビッド・パーナスは、自身の論文で次のように述べています。
We have tried to demonstrate by these examples that it is almost always incorrect to begin the decomposition of a system into modules on the basis of a flowchart. We propose instead that one begins with a list of difficult design decisions or design decisions which are likely to change. Each module is then designed to hide such a decision from the others. (Parnas D.L. (December 1972).)フローチャートに基づいてモジュール化するというのは、大きなプログラムの流れをフローチャートで書いてから、その個々の中身をモジュールとして実装する、というような意味のようです。長~い関数を書いてしまう人がその次のステップとしてやってしまいそうなことですね。
(拙訳)私たちは、フローチャートに基づいてシステムのモジュール化をするのはおおむね不適切だということをこれらの例を用いて示した。その代わりに、難しい設計上の決定や、変更されやすい設計上の決定に基づいて行うことを提案する。これらのモジュールは、それらの決定を他から隠すような設計となる。
モジュール化をするときはそういうやり方をするのではなく、難しいことや変更されやすいことの単位で行うことのほうが効果的だそうです。
では、難しいことって何でしょう。 変わりやすいことって何でしょう。
難しいことや変わりやすいことというのは、往々にして何かの「内部表現」であることが多いです。例えば、内部的にはUTF8を使うのか、Shift-JISを使って処理をするのかとか、リストは配列を使うのか、連結リストを使うのか、などといったことが挙げられます。
なぜならば、やることと言うのはプログラムそのものの存在意味なのでそうそう変わるものではないからです。その実現手段や、内部でのデータ表現の方法はいくらでも変えることができますし、状況が変われば変わり得るでしょう。
そうすると、プログラムを「内」と「外」で分けたくなってくるわけです。内部表現は隠蔽し、外部とのやり取りは定められたインターフェースに則って行う、という形が、モジュール化をする上で大変うまく行く方法だということです。
"モノ視点" で分割する
じゃあどうやって内と外を分けるかと言ったら、1つの方法として、「モノ視点」というのが挙げられるでしょう。例えば自動車。自動車は運転手から見れば「アクセルペダル」「ブレーキペダル」「シフトレバー」「ハンドル」などの決められたインターフェースがあり、それを外(自動車の駆動機構やエンジン等が入っている場所の外という意味)から操作することで、運転手の意図したとおりに加速、減速、曲がるなどといった動作を出力してくれます。ここで自動車の内部の状態(スロットルがどのくらい開いている、燃料の流量はどれくらい、エンジンの回転数は、温度は、シャフトの回転数は…などなど)は運転手がいちいち気を使う必要が無いですよね。
もうわかったでしょうか。
「オブジェクト指向プログラミング」というのは、「モノを中心に内と外を分けるプログラミング」ということなのです。「オブジェクト」が指しているのはインスタンスでもクラスでもなく、「モノ」なのです。
「モノ」と対になる言葉 として「コト」があります。上記の「フローチャートに基づいて分割する方法」は大まかな処理の流れ(コト)に着目して分割するということですが、オブジェクト指向言語はモノの単位で内外を分割してモジュール化をしようという発想が原点にある言語、という意味なのです。
なので、「オブジェクト指向言語ってどんな言語?」と聞かれたら、「モノ視点で内外を分けてモジュール化する言語」と答えれば良いでしょう。
モノ視点でオブジェクト指向3要素を見る
さて、冒頭で紹介したオブジェクト指向の3要素ですが、決してあれを軽視しているわけではありません。当然、「モノ視点で内外を分けてモジュール化する」という原点からいろいろと考えを深めていくと、それらの3要素に行きつくわけです。カプセル化
これは「内外に分ける」ということですね。オブジェクト指向ならモノ視点です。外からインターフェースを介してモノの内部状態を変化させるのがカプセル化です。
継承
これは「モノの分類」に当たります。そもそも「class」という言葉が「分類」ですしね。例えば、自動車は普通車や大型車などに分類でき、大型車はバスやトラックなどに分類でき、例えばバスならば「自動車の基本機能」+「大型車の追加機能」+「バスの追加機能」みたいな関係性があることが言えます。
モノを階層構造で分類し、その差分を表現するのが継承になります。
多相性(ポリモーフィズム)
これは「分類が同じでも実体は別々になり得る」という意味です。例えば同じ「ヒト」であっても一人ひとり個性があって違いますよね。その場合、同じ働きかけ(外からの操作)をしてもふるまい方(結果の出力)は異なるでしょう。
分類としてのふるまいではなく、モノそれぞれでふるまうことができるというのが多相性ということになります。
オブジェクト指向以外のモノの見方
さて、ここまで説明してきたオブジェクト指向ですが、欠点が無いわけではありません。例えばオブジェクト指向を扱ったことがある人ならば、「分類の視点」について悩みを持ったことがあるでしょう。
自動車を大型車、普通車という車種で分類することもできるかもしれませんが、例えばトヨタ車、ホンダ車、日産車…という分類もできるはずです。一つの見方で使うだけならばそれでいいのですが、場合によって見方を変えなければいけないような場合では困ります。
が、多重継承にも様々な問題があり、多重継承構造を認めていないオブジェクト指向言語も少なくありません。
そこで、サブジェクト指向言語というものが開発されました。
「見方によって階層構造が変わってくるから、階層を別に定義できるようにしよう」という発想で、「主題」=「subject」で分割しようという言語です。
Hyper/Jなどという言語があるそうです。
ただ、サブジェクト指向はいろいろな問題があったようです。
そういった背景もあってか、オブジェクト指向のように1つの階層構造をとりあえず作って、様々な階層に横断的に適用させる記述をできるような言語も開発されました。それをアスペクト指向言語と言います。
例えばログの出力コードのような全く階層構造が違うようなクラスに横断的にかかわるようなコードはいろいろな場所に散在しがちです。
こういった関心ごとを分離してまとめるのがアスペクト指向なわけです。
ま、正直この辺りは私はあまり詳しくないのですが。
さて、一通りの説明が終わりましたがいかがでしたでしょうか。
実際にプログラムを書くと、「変わりやすいところを隠蔽」「モノ単位で分割」とか言葉で言うのは簡単でも実際にやるのはなかなか難しいことに気がつくでしょう。ただ、このブログで基本的なオブジェクト指向の考え方はわかっているはずですから、迷ったら原点に立ち返っていろいろと考えていきましょう。
ではでは。