オブジェクト指向なJavaScriptプログラミングのススメ(1)
JavaScript の初心者本で勉強しただけの私としては、jQuery やら色々な人のスクリプトとかを見るにつけ、JavaScript って色々な書き方が出来て不思議な言語だなぁと感じていました。
このエントリーで紹介する Functional vs. Object-Oriented JavaScript Development には、関数型とオブジェクト指向型の2つのプログラミング・スタイルが紹介されていて、私の不思議感を解いてくれました。
ただそれだけなら、ネット上にはもっと詳しい解説をされている日本語サイトがたくさんあります。私が興味を持ったのは、どちらのプログラミング・パラダイムが良いかという文脈ではなく、それぞれの特徴を俯瞰しつつ、また具体例とし jQuery の関数的な側面とオブジェクト指向的な側面にも触れながら、JavaScript の不思議な魅力を語ってくれている点です。
JavaScript にしろ jQuery にしろ、プログラミング・パラダイムに関する深い洞察を元に注意深く作られている、ということを感じたのです。
どのプログラミング・パラダイム、あるいは言語であっても、元となる考え方や思想をちゃんと理解することが、言語を使いこなす上で重要なのは言うまでもないと思います。
そこで JavaScript の2つのプログラミング言語的な側面に言及する前に、それらの考え方の基になる 「構造化分析・設計」 と 「オブジェクト指向分析・設計」 という開発方法論の対比から、それぞれの考え方と物事の捉え方に触れ、前述記事の紹介を試みたいと思います。
ソフトウェアに関してはトウシロの私ですが、Wikipedia の解説 はちょっとピンと来ないという方の、理解の助けになる読み物になれば幸いです
。
1.オブジェクト指向が世の中を救った
1970年台、あと数十年するとソフトウェア開発人口が不足し社会が成り立たなくなるという、いわゆる 「ソフトウェア危機」 が叫ばれていました。が、実際にはそんなことにはならず、現在はその開発量・スピード共に飛躍的に向上しています。
なぜか。
少しでもこの領域に知見があれば、オブジェクト指向のモデリング手法とプログラミング言語の発展がその立役者の一人であることを否定する人はいないでしょう。
ではなぜ、時代を救う立役者になれたのでしょうか。
ある書籍 (タイトルが記憶にありませんが、オブジェクト指向に関する初心者本です) で、オブジェクト指向を一言で次の様に述べていました。
「変わっていく様」 をモデリングするより、「変化し難いもの」 に着目してモデリングする方が、仕様変更や進化に強いシステムの記述が出来る
先の 「ソフトウェア危機」 は、正に旧来の 「構造化分析・設計」 手法やプログラミング言語が時代のニーズに応えられなくなることを予測したものだったわけですが、このオブジェクト指向の 「モノの捕らえ方」 が、仕様変更に強くバグを生みにくい開発方法論やプログラミング言語などの技術を生み出してきた、といっても過言ではないと思います。
2.ソフトウェア工学から見たJavaScript
| 構造化分析・設計 | オブジェクト指向分析・設計 | |
|---|---|---|
| モデリングの着眼点 | 何をどうやって (データ、手続き) |
何がどのように (モノ、振る舞い) |
| モデリングの考え方 | 機能の構造をモデリング | モノの役割、責務をモデリング |
| 機能達成の考え方 | 手続きによってデータに何らかの作用を及ぼすプロセスの集合により、機能を表現する (作用を及ぼさないプロセスは機能を持たない) | 役割を持ったモノ同士の係わり合いにより、価値を表現する (役割を持たないモノは価値を持たない) |
どちらも物事を的確に捉えようと努力することに変わりはありません。現在ではオブジェクト指向の方に軍配が上がるわけですが、そもそも問題領域を構造化して捉えようというアプローチは、人間の論理的な思考にとって自然な事ですし、歴史的に見ても、構造化分析・設計の手法は一定の成果を上げてきたといえるでしょう。

しかし、システムの規模と量の拡大に伴い、構造化分析・設計手法には次のような問題点が出てきました。
- 検証が困難、バグがなくならない
-
典型的にはデータフローやフローチャートを用い、次々に変わり行く様をモデリングするため、
複雑になればやがて人間には追跡が困難となります。さらに仕様変更が加われば、予期せぬバグ
が発生するのも仕方のないことでしょう。
-
典型的にはデータフローやフローチャートを用い、次々に変わり行く様をモデリングするため、
- 再利用できる範囲が非常に狭い
-
上位のプロセスが下位のプロセスを手続き的に呼び出すため、構造的に上位が下位に依存します。
再利用できるのは最下位の、それ以上依存関係のない小さなライブラリ群でしかありません。
-
上位のプロセスが下位のプロセスを手続き的に呼び出すため、構造的に上位が下位に依存します。
時代の流れは、本質的に仕様変更に強く、再利用性の高い開発方法論を必要としたのです。
一方のオプジェクト指向分析・設計手法では、まず 「変化し難いもの」 をそのシステムに登場する 「モノ」 に求め、それらに役割りという抽象性を与えることを基本とします。高度に抽象化を行えば、それはモノの本質に行き着きます。そして 「Dependency Inversion = 依存性の逆転」、つまり 「(より本質に近い)上位のモノは(より具体性に近い)下位のモノに依存してはならない」 という大原則により、上位のモデルも再利用が可能となります。
また役割りを持った「モノ」 達は、その責務をきちんと果たせばシステムが正しく機能するように相互の関連性が記述されます。極端な話、達成手段の詳細は必ずしも公にする必要がなく、役割りさえ果たせばよいのです。これにより、役割りとその関連以上のことで他の 「モノ」 達に影響を与えることがないことを明示できるため、仕様変更の影響範囲が特定でき、役割り単位での検証が成立します。これらよって、大規模かつ複雑なシステムを破綻なく記述ができるのです。

ただし実際問題は、その役割りと責務の範囲について、何をどのレベルで抽象化するかというクラス定義の粒度問題が常に生じ、成果の良し悪しを決める重要な要素になり得ます。
またシステムの隅々までオブジェクト指向で記述されるのが適切かといえば、そうとも限らないでしょう。システムの末端付近では、手続きをそのまま記述した方が検証しやすい、ということも十分にあり得ます。
私はこれを会社組織になぞらえて理解しています。組織の形は、その目的や理念からピラミッド型やフラット型が存在します。組織の役職はいわゆるクラスであり、その役割りを与えられた社員は個々のインスタンスです。そういった役割りを持った人々の関わり合いによって組織の目的・目標が果たされるわけです。例えばあるプロジェクトを実行するときには、どういう人材が何処に必要かという分析と設計が何より大事になるでしょう。
同時に組織には大抵、定型業務というのが存在し、それらは役割りというより業務フローとして定義されている方が分かりやすいハズです。
以上のようなプログラミング・パラダイム的な側面とそれらのメリット・デメリットをプログラミング言語に当てはめてみれば、厳密な規則が設けられた言語系より、必要に応じて柔軟な表記と構成が出来る方が良い、という考え方があっても不思議ではないと思います。
3.プログラミング言語から見たJavaScript
以下、Functional vs. Object-Oriented JavaScript Development 前半部の要約です。
JavaScript は、関数型とオブジェクト指向型の2つの根本的に異なるプログラミング・パラダイムに準じたコードが記述できる非常に柔軟な言語です。
JavaScript プログラミングの最も悪い点は、言語が両方のプログラミング・パラダイムをサポートしているということでしょう。その使い方は使い手に委ねられており、どちらかを強制されるということがありません。JavaScript フレームワークは関数プログラミングを支援するものと、オブジェクト指向プログラミングを支援するものの両方が存在します。
関数プログラミング的な側面
一般に関数型プログラミングでは、サブルーチンコールを基にコードブロックを構築します。上位関数を作るために、関数のパイプライン的な呼び出しを行います。また関数は、変数同様、別の関数に渡される引数としても使われます。
JavaScript では、匿名関数が関数プログラミングの柱となっています。
function(x, y) [ return x + y; }
関数は変数と同等に扱われ、別な変数の引数にすることが可能です。この場合、関数は変数の特別なタイプとみなすことが出来ます。また、通常の関数と匿名関数の違いは、特にありません。変数として使い回すのであれば、名前を必要としません。他の関数との組み合わせ例を次に示します。
// Function to calculate a total
var CalcTotal = function(x, y) {
return x + y;
}
// Function to add taxes
var AddTaxes = function(x) {
return x * 1.2;
}
// Function that pipelines the other two
var CalcTotalPlusTaxis = function(fnCalcTotal, fnAddTaxes, x, y) {
return fnAddTaxes(fnCalsTotal(x, y));
}
// Excution
var result = CalcTotalPlusTaxes(CalcTotal, AddTaxes, 40, 60);
alert(result);
これは次の様に中間に変数を挟まずに無名関数を使うことと同じです。
alert(
(function(x) {return x * 1.2;})(100)
);
匿名関数を単なる変数として扱うことで、非常に効果的な方法でデータと振る舞いをミックスすることが出来るのです。このことにより、依存性逆転の原則
を JavaScript コードに適用することが出来ます。つまり、高次のモジュールがより低次のモジュールに依存するとき、引数として望む振る舞いを挿入するだけで済むのです。多くの場合、このアプローチは、コードの可読性と完結さ、および複雑さを扱う能力を強化します。
オブジェクト指向的な側面
一方、JavaScript は属性とメソッドを含んだオブジェクトも提供し、カプセル化と継承といったオブジェクト指向能力やコンストラクタをサポートします。ただし、純粋なオブジェクト指向言語との最大の違いは、オブジェクトの青写真としての 「クラス」 がないことです。new 演算子は、直接、既存オブジェクトからデータや手続き的関数を継承したオブジェクトを作ります。そのオブジェクトにデータや関数を好きなように追加し、カスタムなオブジェクトとして再利用することが出来るのです。
4.次回は…
jQuery の関数プログラミング的な側面と、オブジェクト指向的な側面のクダリから紹介を続けたいと思います
。
最近のコメント