公開早々、いろいろな方からご意見をいただきました。ありがたいことです。
ここでは、そういったご意見に対する私なりの回答を書いていきたいと思います。
掲示板でいただいた情報ですが、 HAPPY Macintosh Developing TIME!の9/16分に、 このページに対するご意見があります。
このページの作者であるmkinoさんは、私が 『「メッセージ送出」と呼ばれているのは、 実のところ単なる関数呼び出しです。』 と書いたことについて、以下のように書いておられます。
これは同意できない。C++ や Java の説明ならこれでもいいでしょう。 だけど、Objective-C みたいに、動的束縛を利用する言語では、 関数呼び出しとメッセージ送信は明確に区別する必要があるから。
ええと、私はObjective-Cに詳しくないので、 以下の認識が間違っていたらご指摘ください。
こう考えると、Objective-Cでは、関数(メソッド)を見つける部分の手続きは 異なるけれど、見つけた後に行うことは結局 「関数呼び出し」であるように思うのですが、どうでしょうか。
それに、動的束縛があるから関数呼び出しではない、ということなら、 C++やJavaにも動的束縛はあるはずです。 まあ、私がところどころにエクスキューズとして入れている 「継承を考えない限りにおいては」という文言が Objective-Cでは有効ではない、ということならその通りですけれども。
ところで、通常、誰かに「メッセージ」を送って何かやってもらうとき、 相手の作業が終了するのをじっと待つ必要は必ずしもないはずです。 自分は自分で、別の仕事をやっていてもいい。 「こんな説明を読むと、 まるで各オブジェクトにそれぞれプロセスが割り当てられていて、 プロセス間通信で動作するかのように思えますが」と書いたのは、 そういう誤解を招きかねない、という意味で書きました。
実際、私自身、大昔にはそんな誤解をしましたし、 こっちの人も 同じように思ったようです。
だから、まとめにある「「メッセージ」 なんて言葉を嬉しがって使ってる奴はたいてい「知ったか」野郎である。」 は、やめといた方がいいんじゃない?
まあこの文言には煽りも入っているわけですが、 「「メッセージ」なんて言葉を嬉しがって使」うような連中って、 言語がC++だろうとJavaだろうと、継承もポリモフィズムもなかろうと、 「メッセージ」とうい言葉を嬉しがって使っているように見えますよ。 私には。
オブジェクト指向がある程度わかってくると、 「自立的に振舞うオブジェクトが、相互にメッセージを受け渡しながら協調動作する」 姿が見えてくるような気はします(動的束縛とは無関係に)。 でも、それは、「犬と猫と哺乳類」の話に「わかっている人」が頷くように、 「わかっている人」にしかわからない感覚なのでは、と思います。
オブジェクト指向のマルチプルインスタンスの例として、 オセロの盤面オブジェクトを出しているけど、 いやー、これだったらおれも盤面をグローバル変数にするか、 singleton なオブジェクトにするなぁ。 複数のインスタンスを作る状況として、 オセロのネットワーク対戦をやるなら盤面が複数必要でしょ、 って言ってるけど、ちょっと強引。 複数プロセスを立ち上げるほうが自然な感じがしたりして。
えー、私なら、たとえデスクトップアプリケーションとしてオセロを作るとしても、 盤面はグローバル変数にもSingletonにもしないけどなあ。
オセロのネットワーク対戦をやる場合、Servletならプロセスはひとつですし、 現在やってる試合を適当に選んで観覧する「野次馬モード」を付けるにも、 プロセスを分けていたら不便なように思います。
とはいえ、オブジェクト指向の入門的な説明で、 いきなり「ネットワーク対戦オセロ」が出てくるのはどうか、 という気は、私もしないでもありません。 実際JavaWorldに記事を書いたときには、この例を使うことも考えたけど、 そう思ってやめた記憶があります。
こういう説明の例として私が過去使ったのは、以下のようなものです。
うーむ。「疑りぶかい〜」でも、別の例としてこれらの例も使おうかと思っていたのに、 ネタをばらしてしまったではないか。
ある方からメールをいただきましたので、以下に掲載します。 掲載はかまわないが実名は出さないで欲しいとのことでしたので、 伏字にしてあります。
はじめまして、○○と申します。 前橋さんのウェブページにて「オブジェクト指向再入門」を拝見し ました。読みやすい内容ですし、色々と参考になることも多く感謝 しております。 ただ、分かりやすいだけに内容に対する疑問などいつくかの意見も 持ちましたので、メールさせて頂くことにしました。素人の戯言と 考えて読み飛ばして頂ければ幸いです。 オブジェクト指向の本質についてですが、私は、オブジェクト指向 という概念の本質を下記のように考えています。 * インスタンスを中心に考えるということ * 各々のインスタンスが弁別可能だということ つまり、 インスタンスは、自分自身がデータと、そのデータを処理するための能力を保持 している。 インスタンスは、それぞれが別の存在であり別個のデータを保持する。 ユーザは、特定のインスタンスを得、それに指示を出して必要な結果を得る。 ユーザは、そのインスタンスの内部実装を知る必要がない。 といった具合です。 前橋さんの仰る「マルチプルインスタンス」というのは、インスタ ンスの独立性によって生じるメリットのひとつだと考えます。確か にそれはOOPによる恩恵なのでしょうが、本質ではなさそうに思い ます。 * 例え話について 仰る通り、OOPの参考書には意味不明な例え話が沢山出てきます。 それらの多くは、OOPを既に修めた人にとってはそれなりに納得の いくものなのでしょうが、多くの初心者にとっては無意味だと思い ます。 しかし、オセロの例えは… やはり納得がいきません。これはあく まで実装の話であって、OOPの説明ではないからです。私自身がOOP を説明するときは、大抵クルマとその運転操作についての関連を説 明していますが、これはこれで分かりにくいようです。 > 「メッセージ送出」と呼ばれているのは、 実のところ単なる関数呼び出しです 。 > 「メッセージ」なんて言葉を嬉しがって使ってる奴はたいてい「知ったか」野郎であ る。 これは誤解を招く表現ですね。 JAVAは良く知りませんが、確かにC++ではメッセージの伝達を関数 呼び出しの型で実装しています。ただ、これはあくまで言語レベル での実装方法の話であって、OOPの問題ではありません。 rubyやObjective-Cでは言語実装上dynamic bindingによってインス タンスに対し非関数的にメソッドを実行させることができます。ま た同じC++であってもMacOSのPowerPlantや旧BeOSなどのアプリケー ションライブラリでは独自のメッセージシステムを構築して動的な 機能の呼び出しを実装しています。 こういった例への言及もなく、メッセージ機構を「単なる関数呼び 出し」と断じられては困ってしまいます。 > 「オブジェクト指向で再利用性が高まる」と言ってる連中の中には > 、関数(ライブラリ)による再利用性との違いを説明できない奴が多 > い。 まあ、そういう人も多いでしょうけど… オブジェクト指向でコードの再利用が容易なのは、OOPのコード構 築が「インスタンスへのインターフェイス」を介して行なわれ、生 データを扱わなくて済むからです。極端な話、内部のデータ形式が 違うものに変更されてもユーザには関係ないですから。ただし、こ れはC++の様な不完全なOOP言語には当てはまりませんが。 > 「文字列」クラスを継承して 「末尾スペース除去可能文字列」な > るものを作っていますが、 こんなクラス、作ったからといって何 > の役にも立ちません。 たとえばTextFieldというGUIコンポーネン > トがあったとすれば、 そこから取得できる文字列は依然として普 > 通の「文字列」であるはずで、 その文字列からは末尾のスペース > を除去できないからです。 > 文字列末尾のスペースを除去したいのであれば、 そのような関数( > Javaならstaticメソッド)を別途作れば済む話です。 継承では解決 > になりません。 色々なアプローチがあると思いますが、C++でGUIコンポーネントTe xtFieldクラスのインスタンスをaTextField、コンポーネントから 文字列を取り出すメソッドをgetString()、取り出された文字列をS tringクラス、末尾スペースを削除可能な文字列クラスをStringPlu s、削除用のメソッドをremoveLastSpaces()とした場合、 KString aString = aTextField->getString(); aString.removeLastSpaces(); として扱うような実装は可能です。この例では、インスタンスが同 一ではありませんが… またrubyであれば、既存のクラスやオブジェクトにメソッドを付加 することもできます。例えば、 class String def removeLastSpaces; ...; end end と記述するだけで組み込みのStringクラスに機能を追加できます。 これなら同じインスタンスに対して後付けのメソッドを利用できる わけです。 string = aTextField.getString string.removeLastSpaces 確かに、継承がソースの再利用を旨とした機構ではないことには同 意します。継承は同一のインターフェイスを共有するための手段で あり、Objective-Cのprotocolやrubyのmoduleも同様ですから。 > 継承を考えない限りにおいては、オブジェクト指向言語といって > もその程度のものです。 どの程度なのか良く分かりませんが、継承はオブジェクト指向プロ グラミングに必須のものではありません。ここで例示された内容に ついては、 board_put(board, x, y); と、 board.put(x, y); では全く意味が違います。冒頭でも述べましたが、最初の例は「bo ard」という構造体データをput関数で処理するもの、後の例は(も しこれがOOPによる記述であるとするなら)「board」というインス タンスにputという処理を依頼するものです。同じ言語で同じよう に書けるとしても、実装と概念とはまったく別のものだと思います。 ということで長々と批判めいたことを書きましたが、決して前橋さ んのページに対して否定的な意見を持っているという訳ではありま せん。今後も上質な文章の公開を是非宜しくお願いします。
まずはご意見ありがとうございます。
さて、私がどう思うかなのですが。
>* インスタンスを中心に考えるということ >a* 各々のインスタンスが弁別可能だということ
これは、私が書いていることと同じことではないのでしょうか。
つまり、モジュール化による「実装隠蔽」と「マルチプルインスタンス」が 両方揃ってはじめてオブジェクト指向である、と。
そして、「実装隠蔽」は、多少でも目端の利くプログラマなら Cだろうと何だろうとやってきたはずですから、 オブジェクト指向に特有なことといったら「マルチプルインスタンス」である、 と私は思うわけです。
>rubyやObjective-Cでは言語実装上dynamic bindingによってインス >タンスに対し非関数的にメソッドを実行させることができます。ま >た同じC++であってもMacOSのPowerPlantや旧BeOSなどのアプリケー >ションライブラリでは独自のメッセージシステムを構築して動的な >機能の呼び出しを実装しています。
これに関しては、上に書きました。
>KString aString = aTextField->getString(); >aString.removeLastSpaces(); > >として扱うような実装は可能です。この例では、インスタンスが同 >一ではありませんが…
この「KString」は「StringPlus」ですよね。 C++の変換規則によりコンストラクタを暗黙に動かして、型変換を行おうという。
つまり、この1行目の代入は、ぱっと見ただの代入に見えますが、 わざわざ専用のメソッドを使ってStringからStringPlusへの変換を行うという。
可能なことはわかりますが、「末尾空白除去」を行うのに 適切な実装とは思えませんし、正直、「憂鬱な〜」が、 これを想定して書いてあるとも思えません(もしそうなら、 おそろしく説明が不十分でしょう)。
>board_put(board, x, y); > >と、 > >board.put(x, y); > >では全く意味が違います。冒頭でも述べましたが、最初の例は「bo >ard」という構造体データをput関数で処理するもの、後の例は(も >しこれがOOPによる記述であるとするなら)「board」というインス >タンスにputという処理を依頼するものです。同じ言語で同じよう >に書けるとしても、実装と概念とはまったく別のものだと思います。
「全く意味が違う」ようには、私には見えません。
確かに「気持ちの問題」としては、この方のおっしゃるような違いがあるでしょう。 しかしそれはやはり「気持ちの問題」に過ぎないと思います。
『「board」という構造体データをput関数で処理する」』と、 『「board」というインスタンスにputという処理を依頼する』の違いは 「気持ちの問題」でしかないし、 「気持ちの問題」は確かに大きな違いではあるでしょうが、 そう不連続なものでもない。
オブジェクト指向を習得した人なら「犬と猫の哺乳類」に頷くことができるように、 この「気持ちの問題」は、後になって分かってくるものだと思いますよ。私は。
このページに対してご意見・ご質問・ご感想等をいただいた場合、 公開することがあります。