昨日の「オブジェクト指向は難しい」について、 ツッコミ、リンク、Trackbackなどいろいろな反応をいただく。
で、考えたのが、「オブジェクト指向が難しいと思われる原因は、難しいというイメージそのもの」ではないかと。
「オブジェクト指向」のもっとも重要な概念は通常のプログラミングにも登場している。 たとえば、Cで
FILE *f = fopen(path, "r");
という呼び出しは「pathにあるファイルをオープンして、ファイルオブジェクトを得る」という処理そのものだ。 別にオブジェクト指向は必要ない。CのFILE*にはfprintf()やfclose()などのいくつかの「メソッド」がある。
つまり、通常の非オブジェクト指向言語におけるプログラミングでも、 「オブジェクト」という概念は存在していて、無意識のうちに使っているはずなのだ。 このことは別に目新しいことでもなんでもない。K&Rにも「オブジェクト」という用語が使われている。
もっとも多くの言語には「オブジェクト」でない「値」もある。 またCの例を出すと、構造体はオブジェクトではない。代入によってコピーが発生するからだ。 BASICの文字列もオブジェクトではない。
だから、オブジェクトな値(リファレンスあるいはポインタ値)とオブジェクトでない値が混在している言語より、 すべての値がオブジェクトであるRubyのようなオブジェクト指向言語の方が覚えることは少ないはずだ。
ほら、やっぱり「オブジェクト指向は難しくない」。
ただ、問題が残っている。ひとつは従来型プログラミングしか学んでいない人にとっては、 「単なる値」という考え方の方になじんでおり、リファレンスの方は十分に理解されていないらしいのだ。 世の中に「ポインタ解説本」が登場する理由でもある。 つまり、ふたつの考えのうち、ひとつが消えて単純になったはずなのに、残ったのは苦手な方だったと。
なんて不幸な。
やはり、早いうちにLispやRubyやPythonなど、「すべての値がオブジェクト」型の言語を身に付けておくべきではないかと。逆よりも幸福になれそうだ。
オブジェクト指向で2番目に重要な概念も通常のプログラミングに登場している。 2番目に重要な概念とは「動的結合」あるいは「多態(ポリモルフィズム)」である。
harukiさんのツッコミにもあるように、 非オブジェクト指向言語にも引数のデータ型に応じて適宜処理を行ってくれる関数があるものもある。 私が知っているのはLispだが、BASICなどもにあったような気がする。 オブジェクト指向言語と呼ばれるものは、そのような手続きを自分で定義できるようになっただけだ。
ほら、やっぱり「オブジェクト指向は難しくない」。
「多態」を理解していれば「継承」も難しくないはずだ。 現実世界にも結構ある関係だし。
もっとも、あまりに現実世界のモデルにこだわりすぎて(「ほ乳類」と「犬」とか)、 実際のプログラミングにどう使ったらよいのかわからないという点はあるかもしれない。 「差分プログラミング」同様、オブジェクト指向解説書の罪と呼べるかもしれない。 『オブジェクト指向スクリプト言語Ruby』では、私も同じ罪を犯しているけど。
ただ、Rubyのようなオブジェクト指向言語を使っていてもっとも困る点は、 ドキュメントを探すのが面倒という点だ。
あるオブジェクトのメソッドについて知りたい時、
という手順を踏まなければならない。はっきりいって苦痛だ。なんらかの支援が必要だ*1。
Eiffelにはshortとかflatとかいうツールがあり、前者はクラスの仕様だけを抽出し、後者はクラスの継承関係を展開してくれる。これでドキュメントを探し回る苦痛が軽減される。
Rubyにはriとかrefeとかいうツールがあり、ドキュメントを検索してくれる 人間が自分で探すことを比べれば、はるかに楽だ。
%ri each The method named `each' is not unique among Ruby's classes and modules: Array#each, Dir#each, Hash#each, IO#each, Range#each, String#each, Struct#each %ri each Array#each ------------------------------------------------------------- Array#each arr.each {| item | block } -> arr ------------------------------------------------------------------------ Calls block once for each element in arr, passing that element as a parameter. a = [ "a", "b", "c" ] a.each {|x| print x, " -- " } produces: a -- b -- c -- %refe each_line IO#each_line String#each_line StringIO#each_line % refe IO#each_line IO#each_line --- each([rs]) {|line| ... } --- each_line([rs]) {|line| ... } IO ポートから 1 行ずつ読み込んで繰り返すイテレータ。IO ポートはリー ドモードでオープンされている必要があります(open参 照)。 行の区切りは引数 rs で指定した文字列になります。rs の デフォルト値はシステム変数 $/ の値です。 rs に nil を指定すると行区切りなしとみなします。 空文字列 "" を指定すると連続する改行を行の区切りとみなします (パラグラフモード)。 self を返します。
*1 しまった。これは来月のLinux Magazineの記事のネタだった。発売前に書いてしまった
会社から帰宅する時、かなりくたびれていたせいか、自宅直前で 対向車をよけようとして、ガードレールに車をぶつけてしまった。 左後ろのドア周辺にかなり大きな傷がついた。ショック、がっかり。
元々打たれ弱い人間なので、失敗するとかなり落ち込んでしまう。
暗い気持ちで自宅にたどりつくと、心当たりのない荷物が届いている。
なになにNTT-X? これってgooやってるところだよね。
手紙が入っていた。
まつもとゆきひろ様
この度は「gooユーザアンケート」にて、ご協力をいただきまして誠にありがとうございます。
厳正なる抽選の結果「Sonyバイオ」が当選しましたので、送付させていただきました。 今後も、ポータルサイトgooをよろしくお願いします。
草々
なんと、そんなアンケートに答えたことも忘れていたが、VAIO U101当選だそうだ。
びっくり。今まで長いこと生きてきて懸賞なんて当たったことがないのに。 良いことと悪いことが同時に来たのでかなり混乱している。
さて、このVAIO U101をどうしたものか。 今のマシンより速いCPUと大容量のディスクを積んでるんだよな。 こんなに小さいのに。
でも、日常使うにはキーボード小さすぎるんだよな。 少々贅沢な悩み。