「オブジェクト指向プログラミングとは何か?」[Stroustrup87] (PDF, ただし '91 改訂版)
関連:[OOP] オブジェクト指向の概念の発明者は誰ですか?
くだんのオブジェクト指向3点セットの元になったともくされる論文のメモ。抽象データ型に加えて、以下の機構がオブジェクト指向プログラミングをサポートするのに必要とされる。
- 特殊な関数呼び出し機構(仮想関数、動的結合)
- 型チェック機構
- 継承機構
- 多重継承機構
- アクセスコントロール機構
以上は5点ですが、ここから静的型言語(あるいは C++ )に特異的な3点(型チェック機構、多重継承機構、アクセスコントロール機構)が拒絶されて「抽象データ型+ 継承・動的結合」となったのち、「カプセル化、継承、多態性」に変化して定着したものと思われます。アクセスコントロール機構は、いったんは拒絶されたものの、第1項の“抽象データ型”が“カプセル化”に言い換えられるようになったのを契機に、これに組み込んでも(組み込まなくとも)よい…ということになったのでしょう。
抽象データ型は、簡単には「ユーザー定義可能な型」のことです。また、それを拠り所とした問題解決手法や、サポートするシステムの呼称としても用いられます。抽象データ型のスーパーセットしてのオブジェクト指向プログラミングでは、このシステムを実現するためにクラスを流用しています。したがって、抽象データ型のスーパーセットとしてのオブジェクト指向プログラミングにおいては、間違いなく“クラスは必須”ということになります。なお、クラスの(実装の)継承機構を使ったシンプルタイピングに問題があることは、[Cook90] に詳しいです。
抽象データ型のスーパーセットという立場では、「クラス=型」という考え方(よく揶揄されますが…*1)に、まったく支障はありません。支障がないどころか、それを否定したらそもそも話が前に進みません。また、仮想関数(派生クラスで再定義されることが期待される関数)の動作機序がきちんと理解されていれば十分で、その関数の起動に“メッセージ送信”などという概念は無用です。ついでに、余計なお世話ですがw、(メンバー)関数を“メソッド”などと言い換える必要もありません(^_^;)。ユーザー定義型が基本型と同じように扱えることは重要ですが、すべてがユーザー定義型である必要はありません。
これに対してケイの「メッセージ指向」は…というのは、また今度。それと、「メッセージ指向」に“汚染”させずに“(抽象データ型のスーパーセットとしての)オブジェクト指向入門”を書いたら、どのくらい分かりやすくなるか、いつか試してみたいものです。
追記:
おそらく、この切り口、つまり“メッセージ指向に極力汚染されることなしに書いた”「オブジェクト指向入門」(+α)がメイヤーの「オブジェクト指向入門」ということになるのであろうということが、あとで同書に目を通すことで分かりました。ただ、メイヤーは同書で、ほぼ同じ事を言っているストラウストラップの肝心の論文(いわゆる whatis 論文)を黙殺して引用していないのと、Smalltalk システムの設計(この切り口では関係ないはず)に必要以上に批判的なので、個人的にはあまり好感が持てません。
*1:ちなみに、クラスの生みの親である SIMULA 67(後に SIMULA )では「クラス=プロセス」。メッセージ指向では「クラス=メソッドホルダ」…といったところでしょうか。
特異メソッドの例にはどんなのがいいか
特異メソッドというのは、インスタンス特異的メソッドの Ruby におけるローカルな呼び名。Rubyist Magazine - 0002号で、特異メソッドの記事にある七並べを使った特異メソッドの例があまりよくないという話があるようです。個人的には、特異メソッドの一番シンプルで分かりやすい例は「ゼロか、そうでないか」メソッドの実装だと思っていて、好んで使っています。
次は、Ruby が特異メソッドを用意するにあたり、その概念的な参考とされたと言われる CLOS で、インスタンス特異的メソッドを実現する EQL スペシャライザを使用し、「ゼロか、そうでないか」関数“is-zero”(ま、本当にゼロ判定をしている zerop はすでにあるので)を書いた例です。
(defmethod is-zero (self) NIL) (defmethod is-zero ((self (eql 0))) T)
(is-zero 0) ;;=> T (is-zero 1) ;;=> NIL (is-zero "a") ;;=> NIL (is-zero (lambda (x) (+ x x))) ;;=> NIL
ところが、Ruby だとこれが実現できないんですね(^_^;)。
def is_zero; false end def 0.is_zero; true end #=> error
Ruby、駄目じゃん!(ニヤリ)とか思ったら、SELF も駄目でした(爆)。
defaultBehavior _AddSlots: (| isZero = false |) 0 _AddSlots: (| isZero = true |) "=> error "
ま、考えたら当たり前なんですけどね。
オマケ(Squeak の場合)
0 assureUniClass "=> error "
最初の Smalltalk のプロトタイプは 1000 行ほどの BASIC で組まれていた
っていう話は有名なんですが、どこかで見かけた文章の英語がちゃんと読めなくて確認できていなかったんですね。でも、Smalltalk システムっていつごろから仮想マシンを使い始めたんだろう…という話を調べているうちにたまたま行き着いた Smalltalk-80: Bits of History, Words of Advice(通称、グリーンブック)にはっきりと、
The very first Smalltalk evaluator was a thousand-line BASIC program which first evaluated 3 + 4 in October 1972. It was followed in two months by a Nova assembly code implementation which became known as the Smalltalk-72 system.
と書いてあった…というお話でした。で、仮想マシンは Smalltalk-76 あたりかららしいのですが、肝心のこちらのほうはまだちゃんと読めていません。駄目じゃん…。orz
興味があったのは、IBM のメインフレームと LISP と Smalltalk とで、バイトコードインタープリタとして仮想マシンが使われはじめたのはいずれで、それはどれだけ早かったのか; また、遅れて採用したものは先行者の影響を受けているか…という話です。Java VM について、Smalltalk VM の影響を受けてって説明はいいとして、VM が Smalltalk のオリジナルであるかのような書き方をしているところがあったので、それは違うだろ…と、ふと思ったので調べています。