LayeredClass の実装の詳細

JavaScript で private/public の実現 - llameradaの日記で紹介した、LayeredClass の実装方法の詳細について説明する。

HogeクラスがLayeredClassだとする。すると、Hogeクラスのインスタンスは、__privateInstanceというメンバ変数を必ず持つ。このとき、__privateInstanceは、Hogeクラスのprivateメンバと、publicメンバ全てを持っている。つまり、__privateInstanceは、private/publick機能がない場合の、Hogeクラスのインスタンスと同一である。

勘のいい方ならば、ここまでで、実装のアプローチは分かると思う。具体的には、Hogeクラスのpublickメンバは、__privateInstanceの同一メンバを呼び出すだけのラッパーとしている。このとき、privateメンバには、ラッパーを用意しないことで、privateメンバの隠蔽を実現している。

例えば、

var Hoge = LayeredClass.create();
Hoge.prototype = {
  __private: {
    privateMethod: function(){
	alert("public:" + this.publicVariable + "; private:" + this.privateVariable);
    }
  },
  __public:{
    publicVariable: null,
    initialize: function(){
	this.publicVariable = 10;
	this.privateVariable = 20;
    },
    publicMethod: function(){
	this.privateMethod();
    }
  }
}

とした場合、HogeクラスのpublicMethodは次のように書いたのと同じとなる。

Hoge.prototype.publicMethod = function(){
   return this.__public[__publicMethod].apply(this.__privateInstance, arguments);
}

ここでは、apply(this.__privateInstance, arguments)とすることで、Hogeクラス内での this が、全て、this.__privateInstance を指すようにしている。従って、publicMethod内で呼び出しているthis.privateMethod()は、hoge.__privateInstance.privateMethod()になる。

また、__defineSetter__, __defineGetter__ を用いることで、Hogeクラスへの変数のセットも、__privateInstanceへの変数のセットに置き換えている。例えば、

var hoge = new Hoge;
hoge.publicVariable = 15

とした場合、その実態は、

hoge.__privateInstance.publicVariable = 15

となる。

実装の際のテクニックとしては、クロージャーを多様することで、ラッパーメソッドを実現している。

JavaScriptでprivate/publicを実現する方法は、http://www.interq.or.jp/student/exeal/dss/ejs/3/3.html
でもいくつか紹介されている。それらの方法に対する本手法のメリットはpublicなメンバと、privateなメンバの置き換えが容易なことだと思う。どちらのメンバ関数も書式は同一なので、置き換えるには、関数を宣言した場所を変えるだけである。また、private/public機能を切って、オーバーヘッドを減らすのも容易である。一方、本手法のデメリットは、__defineSetter__, __defineGetter__ を使っているため、private/publicが機能するブラウザが mozilla 系に限定されることである。しかし、privateメンバの隠蔽が必要なのは、開発時のみなので、たいした問題にはならないと思う。