なぜ人はオブジェクトの中身を忘れることが出来ないのか

カプセル化」などと念仏を唱えながら、何故人はオブジェクトの中身を忘れることができないのでしょうか。

関数インスタンスもクラスインスタンスも、プログラムを構成するための構造化エンティティ*1です。

クラスとオブジェクトは何に似てるか?

関数はコールするものです。コールとは(Cで言えば)スタックに積んだり自動変数領域を確保したりという「インスタンスの生成」と、そのインスタンスの持つ唯一の手続きの実行、インスタンスの破棄をカプセル化したものです。関数とは「データ」と「手続き」を「手続きそのもの」に見えるように工夫された構造化エンティティと言えます。

クラスとクラスインスタンスは、「インスタンスの生成」と「手続きの実行依頼」を別々に行えるようにした、より汎化した構造化エンティティといえます。関数インスタンスの場合、インスタンス生成とインスタンスへの処理要求が不可分ですが、クラスインスタンスの場合、まず、インスタンスの生成を行い、それとは別にクラスインスタンスに手続きの実行させる、という風にを別々に指示できます。使うのに二手間掛かるようになり、ちょっと面倒くさくなったのですが、トレードオフとしてこの構造化エンティティは複数の手続きや、手続きの実行期間と連動しないフィールドを持てるようになりました。

なので、C の様な構造化言語の経験者には、クラスやオブジェクトは「構造体に関数を突っ込めるようにした物」よりは「より汎化した関数」とイメージしてもらったほうが、良いプログラムを書けると思います。

関数はコールするもの。なら、オブジェクトは?

さて関数が「コール」するものであるように、オブジェクト(クラスインスタンス)は「メッセージ送信」するものです。「インスタンスに処理を実行してもらう」ことを Smalltalkは「メッセージ送信」(一語なのがポイント)と言います。しかし、今や「メッセージ送信」などという用語よりは「メソッドをコール」すると言う言葉の方が好んで使われるご時世です。

でも猫には、「オブジェクトの中のメソッドをコールする」って、ちぃっとも思考がカプセル化できていないように思えるのです。だってOOPでは、そのオブジェクトが実際に該当メソッドを持っているかどうかは関係ないじゃん。そういう意味で、ちまたでよく見かけるオブジェクトの図(「二重丸を書いて外側がメソッド、内側がフィールド)ではなく、青木さんのオブジェクト指向システム分析設計入門オブジェクトの図じゃないとおかしいと思います。(しかし、なんでマイナーなのかしら?この図。)

「メッセージ送信」がこんなにも嫌われ、「メソッドのコール」がこんなにも好かれる理由を猫は理解できません。

C++OOPSmalltalkを経由しない Simula直結型だから文化が違うというのもわかります*2。また静的型付けと「メッセージ送信」がいまいちマッチしないのかもしれません(型チェックを「実装されてるか?」に頼っている以上*3、オブジェクトの中身を気にするのはある意味当然かも、とか)。でもメッセージ送信を避けるあまり、わざわざ話しを複雑な方へ方へと持って行っている気がします。

メッセージ送信という考え方は、それだけで「オブジェクトのカプセル化」と「ポリモーフィズム」を容易に導けます。なのにメソッドコールではどちらも導くことはできないばかりか、まるで反対のことを想像させてしまうのです。

言葉は思考を誘導します。構造体同様の「オブジェクトの内部へ直接手を突っ込んでるぞ感覚」を抜けさせない「メンバフィールドにアクセス」「メンバメソッドのコール」という用語や、C言語由来の「ドット記法」は、そう言う意味では あまり好ましくないと思うのです。


名前重要ですよ。(気にしすぎと言われたら否定できないなぁ)

*1:「構造化エンティティ」という語を知ったのは、たしかオブジェクト指向言語のはなしだったと思います。

*2:しかしC++はあくまで「メンバ関数のコール」です(筋が通ってて気持ちいい)。メソッドはSmalltalk用語で、この混血ヘンテコ語はJavaの罪かしらん。

*3:とか書くと怒られちゃうかな?JavaのInterface は(結局は継承木をつかったドミノ的実装チェックですが)型の概念と実装を切り離す重要なイディオムです。総称は暗黙の型を許す代わりに、コンパイル時に100%チェックできることを保証します。