データ構造はクラスに任せた!だから、アルゴリズムはstatic関数で書くぜ!!(キリッ


きむら(K)さんち経由、実はオブジェクト指向ってしっくりこないんです!。いろいろ強烈なインパクトが満載で、スルーできませんでした。

特に「メンバー関数をstatic宣言すればインスタンス宣言をしなくてもいい」ということ知ってからは、メンバー関数を従来のファンクションのように使っている。共有変数も、pubulic static宣言していまう。したがってプロパティなんて作らない。

staticを理解していない人のコードを見ると、いちいちインスタンス宣言しているので笑ってしまう。

ぎゃーっ....。本人はまわりを笑っているつもりでも、逆にまわりに嗤われていますよ、絶対。


ここ以外にも突っ込みどころ満載のエントリーですので、読みながら突っ込むお手々が止まらなくって、腕がつりそう。そんな突っ込み連打なエントリーもおもしろそうですが、わたしはそういうのは上手く書けないので(性格がネチっこいから、なんだか文章がネバネバしちゃう)、そこらへんはもっと上手い人(例えば JavaBlackさんとか)に期待して(と、さりげなく無茶ぶりしてみる)、わたしは「いちいちインスタンス宣言」の部分にポイントを絞ってネチャッと醸してみようと思います。


* * *


クラスとオブジェクトはもともと Simula の発明品ですが、それは構造体のパワーアップとして作られたわけではなく、「サブルーチン」を拡張して作られました。具体的にサブルーチンをどのようにパワーアップしたかというと、インスタンス(コールスタックに積まれるやつ)の生成と破棄をコール/リターンと連動しないようしたことです。

こんな強化改造しちゃった当初の目的はサブルーチンから(半)コルーチンへとパワーアップ!(というか汎化)させ、擬似並列処理を行うこと。普段サブルーチンを「インスタンスを作っている」なんて意識せずに使っているからピンとこないかもですが、サブルーチンのローカル変数 が進化したのが クラスのメンバ変数 だ! ・・と言えば、この2者の関係が比較的「アハッ!」しやすいかしら。

ここら辺は OOPと自動メモリ管理 - みねこあ にもちっと詳しく書きましたので、よろしく参考*1です。


そんな感じでクラスはサブルーチンを汎化したスーパーサブルーチンなのですが、ということは、逆にクラスから見ればサブルーチンは コールと同時に インスタンスが自動的につくられて(=ローカル変数の領域が確保されて)、リターンと同時にインスタンスが解放される、特殊化(用途が狭められた)されたクラスに見えます。

サブルーチンがそのように特殊化してるのは、もちろんメリットがあってのことです。

ひとつは、実装上の都合──自動メモリ管理にコールスタックを使えること。コールスタックはシンプルで高速な仕組みですが、これは必ず呼び出し元に戻る「サブルーチン」だから使える作戦。そうではない オブジェクトをプログラムの構造化部品として用いるOOPL では自動メモリ管理にガベージコレクタが必要です。

もう一つのメリットは、本質的に分けられないアルゴリズムとデータ構造を、まるで「アルゴリズムだけ分けた」かのように意識させることができること。事実、サブルーチンをさして、「いいや!ローカル変数というデータ構造が常にくっついてる!!」・・とはあまり考えませんよね。つまりアルゴリズムとして抽象化できると言うメリットです。

この二つのメリットのおかげで、サブルーチンはその「インスタンス」をまったく意識しないでプログラミングできます。意識しないということは、それだけ「ライトウェイト」ということです。この「重さ」は、オブジェクト指向プログラミングがどうしても持ってしまう大きなハンデです。

なので、OOP になって「いちいちインスタンス宣言」ウザい──という感覚は、ごもっとも。


* * *


とはいうけれど、この方の言っていることに同意できるかというと、JavaBlackさんの毒を期待してしまうくらいアレなわけでして。

どうしてもこのエントリを読むとついつい脳内で「キリッ」と保管してしまうのは、

オブジェクト指向、結局のところホントにモノ(オブジェクト)に使われている記法、例えばGUI コンポーネント、データベース、ファイルなどであって、プログラムのアルゴリズムとは無関係のものである。

(キリッ..と強調したのはわたし)

みたいなところ。つまり「オブジェクト=データの入れ物」でしかないと勘違いしてしまっていること──「わたしはオブジェクトの使い方のうちのたったひとつ、『データ構造を表現すること』しかできない(知らない)んです」と、暗に告白しているところが「キリッ」っぽくなるポイントだったり。

クラスとオブジェクトは、一つで(Cで言う)「関数」と「構造体」の両方に使えてしまう汎用的な プログラムの構造化部品です。けれども、なぜだか「進化した構造体」的な使い方ばかりに注目して、「進化した関数」として使わない・・というかそもそも知らない、というのは本当にありがちなオチなのですよね。

オブジェクトをデータの入れ物としてしか使わないのであれば、インスタンスを作るメリットも、それは無いよなぁ・・と妙に納得です。トホホ。


* * *


「手続きプログラミング」というパラダイム──データと、それに対する操作を別々に記述(構造化)するプログラミングパラダイム は、一つのパラダイムではなくって「それがコンピュータプログラムというものだ」みたいに思われています。なんだか「The」がつきそうな勢いです。

特に、サブルーチンのあたかも「ロジックだけ」のフリをする演技は実に見事で、それが演技だとは滅多に気付かせません。だかこそ手続きパラダイムがこれだけわかりやすいものとなったのだと思います。

で、「ありがち」の原因は、やっぱり人も世の中も「手続きプログラミング」にあまりに馴染みすぎていることなのかな、と思います。

前々からおもっていたのは、 C++ な構文がよくないみたいな話。C++ の文法って、真っ白な紙に黒いペンで手続きパラダイムな文法を書いた後、赤いペンでOOPな文法を重ね書きしているような印象を受けます。学校の先生で、黒板を使い切った後、消さずに違う色のチョークで重ね書きしちゃう方が、たまーに居らっしゃるけれど、ちょうどそんなかんじ。

C++ は重ね方が上手いから、2色のノートを白黒コピーとっても破綻しない、そんな神が降りたようなところがあるのだけれど、それが逆に、あたかもOOPを「手続きパラダイム」を構成部品に、それを組み合わせて 肥大成長させたモノのように見せてしまう。(たとえば、メソッドが手続きの関数と同等品にみえるような)──上手さゆえの悲劇というか、もうちょっとムリムリ感があった方がかえってよかったのかも・・・と思わなくもありません。(詳しくはハイブリッドOOPLの呪い - みねこあを参考リソースでお願いです)


あと、今回改めて考えて思いついたのが、手続き言語では データのインスタンスは意識する(≒メモリ管理を意識する)けれど、サブルーチンのインスタンスは意識しない のが普通だったので、逆に「インスタンスを意識するもの は データ(のようなもの) である」と風に無意識下で捉えてしまうのかも?、ということ。

これは鶏と卵ではあるけれど、サブルーチンのインスタンスを意識しないのは、それが本質的にそうだからではなく、単にサブルーチンが上手にインスタンスの存在を隠蔽しているからであって、逆にそれがサブルーチンの「出来ること」に制限をかけてしまっています。せっかくその制限を「クラス」と「オブジェクト」で取っ払ったのに、心にその制限が染み付いてしまっていて、無いはずの壁の前でパントマイムしている...。

で、そのパントマイムの内側の世界だけではクラスは「進化したデータ構造」でしかない。だから処理の記述においては、トレードオフで失った「インスタンスを意識しないプログラミング」の分を単純に損しているだけなので、それじゃあ OOP 不用論も唱えたくなるかもだわ、と思います。



逆に いまどきのOOP好きの多くが ポリモーフィズムを ついついOOP の大事なポイントとして強調してしまうのは、それがオブジェクトの「進化した関数」の側面そのものだから、というのがあると思います。別に「進化したデータ構造」な面を軽視しているわけじゃないけれど、そっちはみんな自然に気付くから。

進化した分だけオブジェクトにはサブルーチンには出来ない処理の構造化が出来るわけで、その結果得られる力は、たとえアルゴリズムを書くシチュエーションであっても「いちいちインスタンス宣言」する対価を払って余りあります。その力は大規模とは無縁なちょっとしたスクリプトを書くときだって OOP だと楽だと感じさせてくれるのです。(Ruby かわいいよ Ruby


* * *


OOPを「手続きパラダイム」を単に拡張したもの、なぁんて言ってしまう人は、本当にOOPLを使っても そういう風にしかプログラミングできていない、と言うお話でした。

*1:最近流行りの「参考リソース」というやつですね