Hot Heart, Cool Mind.

会計×IT の深層へ

2つのオブジェクト指向ーメッセージ・パッシングと抽象データ型

オブジェクト指向には、もともとアラン・ケイが創案したメッセージ・パッシングのオブジェクト指向と、その後、抽象データ型から発展したオブジェクト指向の2つがあって、その2つは、プログラム言語の機構の面からは共通する要素も多いのだけどプログラミングに対するビジョンという点では相当に異なっている、という見方があります。

これに関しては、id:sumim さんが「二つのオブジェクト指向とそれぞれのメリット」という行き届いた論考を公開しておられて、僕などはそれを読んで頭を整理した口です。

その後、それを踏まえて自分なりに両者の違いを考えてツイートしましたが、言葉足らずな点もあったので、内容を整理するとともに加筆しました。

言語機構に惑わされるな

この2つのオブジェクト指向は、実現する機構の面では似ています。オブジェクト指向の3要素として「カプセル化、継承、ポリモフィズム」があると言われますが、そうした言語機構は、抽象データ型志向の C++であれ、メッセージ・パッシング志向の Smalltalk であれ、備えています。だから、2つのオブジェクト指向といった区別がそもそも議論の対象となる理由さえわかりづらい。

両方のオブジェクト指向言語が、ある意味、収斂進化してきて、似たような機構を備えるようになったのかもしれません。逆に言えば、だからこそ、上記の3つが「どんな」オブジェクト指向言語にも共通する特長と認識されるようになったのかも。

一方で、抽象データ型のオブジェクト指向とメッセージ・パッシングのオブジェクト指向では、プログラミングのパラダイムと言いますか、実現しようとしている世界の姿はかなり違うように僕には思えます。言語機構の面からではなく、むしろ、そちらの方から見た方が、両者の違いがわかりやすいように思えます。

メッセージの送り手と受け手の関係性

両者の違いを理解する視点のひとつ目は、メッセージの送り手と受け手の関係性です。

抽象データ型では、メッセージの送り手は受け手であるオブジェクトの「ユーザー」です。なんらかの仕事を受け手に依頼し、その仕事が厳格に実行されることを期待します。そうした期待が「契約」として表現されます。抽象データ型オブジェクト指向の旗手のひとりであるメイヤーは「契約による設計」の発案者でもあります。

抽象データ型である Stack に何かの値を push したら、その値がスタックに積まれ、次に pop したときにそれが取り出されることが期待されます。こんな風に、厳格に規定された仕事を受け手が引き受ける、というメンタルモデルが、抽象データ型の根底にあると思います。

一方、メッセージ・パッシングのオブジェクト指向では、メッセージを送られた側が何をするかは、基本的に、送られた側の判断事項です。たとえば、Observer パターンで、変更の発生をSubject から通知された Observer が何をするのかは Observer の自由であって、Subject 側は関知しません。

別の例を挙げましょう。Commandパターンで、Command の execute() を呼ぶ側は、実際にどういった処理が実行されるのか、関知しません。Visitorパターンで、Visitor が何をするかは、Visitor の問題であって、呼び出す側は関知しません。こういう関係性は、メッセージ・パッシング的だと思います。攻撃を受けたモンスターがどれだけのダメージを食らうかは、攻撃する側の決定事項ではないのです。

メッセージ・パッシングのオブジェクト指向でも、送り手と受け手の間に「契約」はあると言えばありますが、それは、受け側の振る舞いをほとんど拘束しない、非常にユルい契約です。

ポリモフィズムに対する態度

もう一つの視点は、ポリモフィズムに対する態度です。抽象データ型のオブジェクト指向において、ポリモフィズムは本当は必須ではありません。というのは、抽象データ型において重要なことは、インターフェース(契約)と実装を分離することであって、振る舞いを動的に差し替えることではないからです。

抽象データ型のオブジェクト指向では、むしろ、実装を差し替えたとしても振る舞いに影響を与えないことが強く要請されます。Javaコレクションフレームワークにおいて、List という抽象データ型を実装する ArrayList と LinkedList は、実装が異なるにもかかわらず振る舞いに差がないことが重要なのです。

List という抽象データ型の狙いは、配列による実装を連結リストによる実装に変更しても問題なく動くコードを書けるようにすることです。それが「データ構造ではなく振る舞いによってデータ型を定義する」という抽象データ型の目標です。

この例から、抽象データ型においてポリモフィズムは便利ではあるけれど必須ではないことがわかります。実装を差し替えるには、ポリモフィズムを用いなくとも、Listを具象クラスにしておきそのコードを書き換えることも可能だからです。

一方で、メッセージ・パッシングのオブジェクト指向ではどうでしょう。受け手が何をするかは受け手次第、という世界を実現するために、ポリモフィズムは必須です。 ポリモフィズムが無ければ、先に例として挙げた Observer パターンも Command パターンも Visitor パターンも使えなくなってしまいます。

じっさい、ポリモフィズムがなければ GoFデザインパターンのかなりの部分が壊滅します。これらのパターンは、抽象データ型の枠組みに立脚するものではない。むしろ、メッセージ・パッシングのオブジェクト指向に由来しています。

抽象データ型のオブジェクト指向の視点からすれば、Command や Visitor は極めて説明しづらい存在です。なにしろ、これらがデータを持っているかどうかさえ定かではないのですから。

違いは何に由来するのか

以上、抽象データ型のオブジェクト指向とメッセージ・パッシングのオブジェクト指向の違いを、①メッセージの送り手と受け手の関係性、②ポリモフィズムに対する態度、という視点から説明してみました。こうした見方は分かりやすいのではないでしょうか。

そして付け加えるならば、この2つの違いは、両者の狙いの違いに由来しています。抽象データ型のオブジェクト指向の狙いは、ひとことで言えば「情報隠蔽」です。オブジェクトの利用側に、知らなくて済む詳細を開示しないことです。さらに言えば、その背後には、プログラムを堅牢にするという意図があります。メイヤーの著書を読めば、契約による設計も含め、彼が、プログラムを堅牢にすることについて持つ並々ならぬ情熱が伝わってきます。

これに対して、メッセージ・パッシングのオブジェクト指向の本当の狙いは、全体を構成する各部分が実行時に結合され、その結合のされ方も、ユーザーの振る舞いに応じてダイナミックに変わっていくような、非常に動的なソフトウェア構造を作り出すことでしょう。

アラン・ケイは、オブジェクトの各々がネットワーク上のコンピュータであるようなイメージを持っていたと書いています。多数の計算機がゆるく連携して何かの結果を出すシステム。その連携の仕方はユーザーが決める。Webみたいじゃないですか。Webにおける結合材であるハイパーリンクは静的ですが、それを、より動的なメッセージ・パッシングに置換したイメージ。マイクロ・サービス(ボソッ。REST(ボソボソッ。

メイヤーたちの抽象データ型のオブジェクト指向は、プログラムは職業的プログラマーが書くことを前提にプログラムの品質の向上を狙ったものでしょう。アラン・ケイのメッセージングのオブジェクト指向は、そうではなく、ユーザーとコンピュータの関わり方の進化を狙ったもの、すべての人をプログラマーにすることを狙ったものであるような気がします。もちろん、その成果を僕ら職業的プログラマーが享受してもまったく構わないし、デザインパターンの例でわかるように実際に享受しているわけですが。

そうだとすれば、2つのオブジェクト指向は、そもそものビジョンからして違うわけです。すれ違うのは当たり前ですよね。

僕は実務家であって、抽象データ型もメッセージ・パッシングもそれほど深く研究しているわけではありません。間違い・誤解もあると思いますが、シロウトなりにひとつの理解を紹介することにも意味はあるかと思って書いてみました。

皆様の思索の材料になったでしょうか。