インターネッツを良くするポエムというのは、「こういう問題に対して、こういうソリューションでカバーしてきたよ」をみんなでシェアすることだと思うので、ここに挙げられていることの一部に対して、オブジェクト指向界隈が今までこんな工夫をしてきたよとか、僕の目から見えている「技術発展の流れ」について書いてみようと思います。まあ僕も全ジャンルをまんべんなくやっているわけじゃないし、一部想像で補っている部分もあります。他にもあればぜひシェアしてください!
上記のサイトで書かれている内容のうち、
- オブジェクトのつながり具合が手続きでしか表現できない/知識表現が手続き側に偏っている
- 関係性が表現できない
- ユーザレベルでの部品化再利用に全然なっていない
について取り扱います。
オブジェクトのつながり具合が手続きでしか表現できない/知識表現が手続き側に偏っている
元は2項目ですが、内容的に似通っているかのように見えたのでまとめました。オブジェクト指向も、メモリ中にオブジェクトの構造ができあがってしまうと、後はメッセージパッシングでやりとりするだけになるので、別に難しくもなんともないのですが、コードを見ると、オブジェクト階層を組み立てるコードが大量にまざってきてしまって、コアのロジックのS/N比が下がってしまいます。
これに関してはいくつかソリューションがあります。
データ駆動
データ駆動というと、ビッグデータとかディープラーニング的な文脈(データ駆動型イノベーション)とかでも使われるんですが、ここで触れるのは、その前から使われてきたデータ駆動テストとかのデータ駆動です。簡単に説明すると、コードだけで書くのが難しいとか、重複が多いものをCSVなどの外部データから取り込んで使うというものです。
例えば、Unity3Dとか、Unreal Engine4のワールド、(死期が近い)Flashのムービークリップなどのビジュアル要素を考えてみると良いでしょう。実行時のワールドやステージ上にはさまざまなオブジェクトが置かれます。これらのオブジェクトはプログラムを使っても組み立てることができるものです。それぞれ、ソースのファイルをテキストエディタで直接触ることはしません。ユーザは、ビジュアルに、オブジェクトのデプロイ(本番環境に反映ではなくて、UMLの配置図の配置の意味)の仕方などを定義し、ゲームエンジンやランタイムがこのデータを読んで実行開始時にオブジェクトを作り、エディタで設定されたパラメータを設定していき、AIで動き出したり、ユーザ操作に応答したり、アニメーションしたりしはじめます。
これらの仕組みによってコードから、複雑な木構造を持つオブジェクト構造を作る手続きを追い出すことができます。これらの仕組みによって「プログラマ以外が世界を作れる」という分業も可能にはしてくれますが、今回の話とは関係ないのでここは深掘りしません。
ディスパッチのタイミングとしてはこのようなローダーで行う以外にもあります。Qtのuicという処理系を使うとC++のコードを生成します。実行時のCPUから見れば、人が書いたコードかどうか区別はできませんし、生成されたコードを見てみれば手続きの羅列なんですが、「組み立てコードを手続き的に人がやらなければならない」ということからは解放されます。
依存性注入(DI)は、コード量を大幅に下げるものではないため、多少目的は違うかもしれませんが、オブジェクトを作るファクトリコードを追い出すという意味では親戚かな、と思います。
PySpaメンバーからの補足
データ分析では昔からデータ駆動だったようです。どのようにデータ処理のパイプラインをつなげていくかをビジュアルに定義。機械学習は、trainとpredictionでほぼ同じ構造(データフロー)を使うから、グラフ構造を再利用しないといけないので、こういうツールを使えると、ミスがなくなるとのこと。
あと、最近Infrastructure as a Code(IaaC)も話題になっていますが、インフラ整備にもビジュアルツールがあります。
仮想DOM(リテラルの活用)
極端な話、全部コードでウェブサイトを組み立てることもできます。下記のコードはnewそのものは使ってないですが、ファクトリメソッドを使ってテキストノードを作り、親ノードにリンクしています。
document.body.appendChild(document.createTextNode("テキスト"));
でも、普通はこんなことはしなくて、HTMLで書きますよね。ただ、HTMLだけでは柔軟性が低いため、HTMLのテンプレートが良く利用されます。サーバサイドのテンプレートはHTMLを生成し、ブラウザがそれを解釈してDOMのオブジェクト階層の各要素をnewします。HTMLだけ見ればデータ駆動と言えなくもないかもしれません。
「組み立てる手続きを見えなくする」というのは、乱暴な書き方をすれば「newの隠蔽」です。2年ぐらい前からHTMLというテキストを経由しないDOMの生成方法が話題になりました。
類似したものに仮想DOMというものもあります。仮想DOMが技術的に一番解決したかったのは「JavaScriptによるDOMの生成が遅い」ことだとは思いますが、「リテラルで書くことで、動作可能な状態のオブジェクトの木構造を組み立てるコードを書く手間」を省くという手法を一般に広めたという功績もあると思います。リテラルで定義するのはそれまでも何度か見かけたことがありますが(関数呼び出しのネストでGUIを定義するBeOS用のフレームワークとか)、一般的とは言えなかったと思います。今までやりにくかったC++のリテラルのオブジェクト定義も、C++11以降の初期化リストを使えばだいぶ書きやすくなります。いろいろな言語に波及していくんじゃないかと思います。
まあ、JSX(Facebookの方)にしても、HTMLのテンプレートにしても、デザイナー的スキルによる完成形のHTMLの作成と、プログラマー的スキルによるオブジェクトツリー構築の手間の改善の両面にまたがっていて、まだまだ改善の余地はありそう(僕自身、アイディアはあるが時間はないので手が付けられてない)な領域ではあります。
アノテーション/設定より規約
オブジェクト指向とは直接関係はないかもしれませんが、手続きから宣言へ、ということで2つ紹介します。1つがアノテーションです。アノテーションは確かJavaが最初で、最初はJavaDocに書き込まれたテキストだったものが、途中から公式な文法に昇格したものです。他にもPythonがデコレータとして設定しています。C#も持ってますね。
アトリビュートが、ミクロなオブジェクト単位での属性の設定の方法だとすると、よりマクロなものが「設定よりも規約」というルールです。名前やディレクトリ構造など、コードの外側の属性を使って、オブジェクトの組み立て/配置を自動化します。コンパイル言語よりもLLとか動的言語の方が多いですかね。
関係性が表現できない(関数型のエッセンスの利用)
オブジェクト指向と二元論的に語られることの多い関数型ですが、関数型自体はオブジェクト指向と直交したものだと思います。
念のため、書いておきますが、Wikipediaの関数型言語一覧を見ると分かるように、「関数型言語の強み」としてよく言われる「純粋だから副作用がない」とかもろもろは一部の言語の特徴でしかないようです。少なくとも僕が書いたことある言語(Erlang)はそれほど安全なわけではなく、すぐクラッシュしました。なので「JavaScriptで関数型のコーディングなんて(ry」という話は今日は置いといてください。ここまで書いてもタイトルだけ見て脊髄反射してくる人はいそうな気がしますが・・・
関係性のうち、静的な親子関係や配置の関連については最初に書いたデータ指向でカバーされている内容かと思います。ここではAオブジェクトがこうなったら、Bオブジェクトはこうなる、みたいな動的な関係性について紹介します。
リストを取り扱いやすいようにする
これは上記の問題で定義されていたものではありませんが、多くのオブジェクト指向言語が取り入れた&次の話の導入として紹介します。
C++98のSTLのイテレータはお世辞にも使いやすいとはいえませんでしたが、たくさんあるデータの取り出し、加工等を使いやすくしようとする文法はどのオブジェクト指向も備えています。ループの要素をたどるループだけではなく、リスト内包表記、LINQ、いろんな言語のforEach/map/reduceなどなど。
jQuery
設計手法ではなくて、ライブラリ名ですが、多くの人が使ったことがあり、影響を与えたということでライブラリ名のまま紹介します。DOM API標準にまで影響を与えたぐらいなので、JavaScript史に永遠に残る功績を残しています。
jQueryは大規模に副作用のあるようなコードを書くと徐々に大変になっていくことで最近は別の方法も利用させるようになりましたが、副作用の起こし方をコントロールして使うのであれば、未だに強力なAPIです。内容的にはリストを取り扱いやすくする
jQueryはパターンでマッチさせて、パイプ(ピリオド)で繋いだ処理に次々と流していくことでウェブサイトに変化を与えます。途中でさらにパターンマッチをさせることもできますし、結構柔軟です。イベントに応答して処理がトリガーさせるようにも書けます。JavaScriptプログラマーが関数型言語に興味を持つようになり、ScalaやHaskell、OCamlなどを触る気にさせたり、関数型ブーム(n年ぶりm回目)を先導した影の立役者だと思います。
リアクティブ
イベント駆動+関数型+アルファがリアクティブです。オブジェクトが何らかの反応をした時に、それがどのようにどのオブジェクトに波及していくかを宣言的に定義したものです。元はC#のためにMicrosoftが作ったものですが、Java/JavaScript/Objective-C/Swiftなど、多くの言語に波及していきました。
リアクティブもjQueryと同じように、処理を連携させることもできますが、逐次処理というよりも、イベント起点の処理にフォーカスしています。これも、オブジェクト間の関係を宣言的に記述するためのものと言えます。
リアクティブもやっぱりjQueryの影響を受けているんでしょうかね?
ビジュアルなノードベースの記述言語
先に紹介したIaaCのツールでは、ビジュアルにジョブの依存関係を持たせて記述することができます。Future/Promise, async/awaitとかもあったりしますが、複雑に分岐・合流するロジックを行・列のテキストで書くのが必ずしも分かりやすいわけではないということもあります。
依存関係が一方通行であれば左から右に流れるので、とても見通しが良く書くことができます。世の中には前後のノードが相互作用をし続けるという、コードで書こうとすると気が遠くなるようなものもあって、電子回路とか制御回路がそれにあたります。MatlabなんかはこれをSimulinkというツールでサポートしていますね。
ユーザレベルでの部品化再利用に全然なっていない
パッケージマネージャ
元エントリーでは、部品レベルの再利用が進んでない、という話がありました。確かに、QtのQObjectとかQStringを利用したクラスを、他のC++のプロジェクトにそのまま入れるとかなかなか頭が痛い話だったりします。オブジェクト指向系のライブラリはランタイムもでかいので、一見閉じているようで、なかなかヘビーなライブラリレベルの依存関係をもたらしがち。
とはいえ、C++以外はあまりこの問題はないかなと思っています。言語ごとに、CPAN/pypi/gems/PEAR/npm/CocoaPods/Maven Repository/Cargoなどのパッケージマネージャが用意されることが当たり前になっています。ウェブ・アプリケーション・フレームワーク、モバイルのiPhone/Androidアプリなど、文脈を固定すれば構造やレイヤーはある程度固定化されます。ウェブもPythonのWSGIを発端として、各言語で仕様化が進み、ミドルウェアをプラグインしたり、ということも増えてきていますよね。動的言語だとそもそもダックタイピングなので「◯◯を継承しないと」ということもないですし、Golangなんかはダックタイピングに近いインタフェース指向なので他から持ってきたライブラリを合わせこむのもそんな難しくないです。
C++もパッケージマネージャを作ろうという動きがあったりなかったりするんですが、biicodeが昨年死に、後はgithubに乗っけてちょっとコードを取ってくる系がいくつかあったりという状況ですね。僕もQt+CMakeを前提としたやつを作ってドッグフーディングしてます。コード&依存ライブラリを取ってきて、インクルードパスを通すというのさえ自動化できれば、今のIDEは昔よりも遥かに優秀ですので、かなり快適です。
リフレクション/メタプログラミング
リフレクションなどをどこに入れるか迷ったのですが、とりあえずここに入れておきます。リフレクションを使うと、呼ぶ側は多少重くなりますが、呼ばれる側はシンプルになります。呼ばれる側は、他のクラスとの関係を記述しておかなくても、インタフェースをむりやり合わせて実行できます。僕がひどい(褒め言葉)だと思ったのは、JRubyのcamelCaseのメソッド名をRuby風にsnake_caseでも呼べるようにするというハックとかですね。
再利用性も、「言語」や「処理系」という枠を超えた「フレームワーク」が力を持つようになり、一段レベルが上がった気がします。JVM系言語ではJavaのライブラリが使えます。CLRも同様ですね。最近だとCとの親和性の高さで用途が最初から広かったGoもあります。Goはarchive/c-sharedへのビルドも出来て、他の言語から利用する方向にも芽を出し始めています。Swiftも、Objective-CのCocoa資産をまるごと取り込むことで一気にメジャー言語になりました。Erlang OTPが使えるElixirもそうですかね。
元エントリーの「オブジェクト指向の部品レベルの再利用が進んでない」とはちょっと違うレイヤーですが、「一度学んだ知識が言語をまたいで再利用可能」というのは悪くないことですよね。
まとめ
上記のエントリーではオブジェクト指向が4つあると書きましたが、15-10年前に圧倒的に強い影響力を持っていたのはクラス指向だったと思います。デザインパターンがあり、テスト駆動開発があり・・・ただ、「デザインパターン」が時代を超えて進化することはありませんでした。パターンな人たちは新しいパターン・ランゲージを作って別分野に広げるのにお熱でしたが、動的言語時代にその知見はあまり生かされなかったように思います。その後CASEツールやMDAなど上流側に行こうとしてそのまま消えてしまったようです。どちらかというと超大規模にフォーカスしようとしたけど、でSOAのハブとなる WebSphere MQはおいくらなんですか?◯億円ですね、みたいな世界でみんなついてこなくなったと。AOP?知らない子ですね。
なので、僕のエントリーにもコメントでいくつかついていましたが、クラス指向なオブジェクト指向を中心とした用語は10-15年前からあまり変わってないように思えます。オブジェクト指向というと、分析や設計の話も大分話題になったんですが、結局今はあまり活発ではなくて、高い値段でIBMに買われたRational Roseももはや存在せず、リファクタリングをしつつ依存関係を綺麗に整えてくという漸進的な設計ぐらいしかなくて、オブジェクト指向≒オブジェクト指向プログラミングとなっているという理解。あ、もちろんエンティティの設計みたいなのはありますが、どちらかというとOOPというよりも、RDB的な分析設計かなと。
とは言え、新しいテクニックもいろいろ活用されるようになってきました。ここに書いたものはデザインパターンには入っていないけど、パターン化されてもおかしくないようなものばかりだと思います。他にも、別の言語の処理系を乗せてフットワークを軽くする(Qtに搭載されているJavaScriptのマクロエンジン、h2o等に搭載されているmruby、nginxのlua)というのもあります(うまく分類できてないので書いてないですが)。もちろん、これら以外にも、僕の知らないものもたくさんあると思うので、思いついた方はポエムタグで(ry
いろいろ書いてきて思ったのは、クラス構文がなかったために長らく「オブジェクト指向言語として二流」扱いされてきたJavaScriptが意外と充実しているな、ということです。クラス指向の小難しい用語(多くの人に伝わらない)の議論とかとは無縁でいたお陰で、逆にそのクラス指向の陥っていた進化の袋小路に陥らずに、自由な発想で生産性向上にフォーカスしてきたのかなと。今後もJavaScriptは破壊的に世界を巻き込んでいくんでしょうけど、ちょっと引いた所から注目していこうと思っています(火中の栗を拾うのは若者に任せます)。