前のページ(プロトタイプベース)へ / 次のページ(言語仕様の比較)へ
クロージャベースのオブジェクト指向プログラミングについて考えます。 クロージャベースのオブジェクト指向プログラミング言語だと 明言しているものは見つからないので クロージャをオブジェクトとして使う方法を考えようという話です。
まずはクロージャについておさらいしましょう。 クロージャとは自身が定義されたローカル環境の変数を その内部から操作出来るという機能を持った関数のことです。 必然的に静的スコープのプログラミング言語でのみ成立します。 その関数に名前が付いているか否かは本質的ではないと私は考えます。 もちろん無名関数を定義出来る言語の方が表現が簡潔になるので 言語仕様としては好ましいとは思いますが。
クロージャが関数に環境を加えたものなのと オブジェクトがデータ構造に操作を加えたものなのとは似ていませんか。 ちょっと主従が逆ではありますけども。 例えば JavaScript で引数として数を与えると 今までに与えた数の総和を返すものを作ってみましょう。 クロージャ版は
var acum = (function() {
var sum = 0;
return function(x) {
sum += x;
return sum;
}
})();
alert(acum(1));// 1
alert(acum(2));// 3
alert(acum(3));// 6
という感じになります。 見た目は関数ですが状態を持っていることが判ります。 一方オブジェクト版では
var acum = {
sum:0,
inc:function(x) {
this.sum += x;
return this.sum;
}
};
alert(acum.inc(1));// 1
alert(acum.inc(2));// 3
alert(acum.inc(3));// 6
のようになります。 見た目はデータ構造ですが操作を持っていることが判ります。
さてどちらが良いかというと難しいところです。 内容の複雑さで言えば大差ないのではないでしょうか。 クロージャ版は sum の存在が完全に隠蔽出来るという利点がありますが スコープ・チェインについては暗黙の了解としているという欠点があります。 逆にオブジェクト版は inc を共有出来るという利点がありますが this の存在については暗黙の了解としているという欠点があります。 関数型言語に慣れた人はクロージャ版を選ぶかもしれませんし クラスベースやプロトタイプベースの オブジェクト指向プログラミング言語に慣れた人は オブジェクト版を選ぶのではないかとも思います。
クロージャは一つの関数が環境を持ったものという経緯から 一つのデータ構造に対して複数の操作を定義することが苦手です。 そのような動作には動的ディスパッチする必要があるのではないでしょうか。 JavaScript で具体的に書くと
var obj = (function() {
var val = 0;
var inc = function() {
val += 1;
return val;
}
var dec = function() {
val -= 1;
return val;
}
return function(msg) {
return eval(msg);
};
})();
alert(obj("inc")());// 1
alert(obj("inc")());// 2
alert(obj("dec")());// 1
alert(obj("val"));// 1
のようになります。 ちなみに動的ディスパッチは eval() で済ませていますが 丁寧に書けばアクセス制御も簡単でしょう。 実装の共有が出来ないので継承はちょっと難しいかもしれませんが。
this を使わないでもオブジェクトが再現出来たように クロージャベースのオブジェクト指向プログラミングは可能です。 やはり JavaScript でそれを実現するのには無理がありそうです。 例えば Scheme ならマクロが強力なので可能だと思います。 既にそんなライブラリがあるとは思うのですが特に探してはいません。 ごめんなさい。