blades.jsの大きな特徴のひとつが、クラスのカプセル化、つまりメンバの隠匿を(限定的にではあるが)実現している点です。

produced by Blades co.,ltd.



Blades.js

Rating ---

  • 1
  • 2
  • 3
  • 4
  • 1

Tips

カプセル化-blades.jsにおけるメンバの隠匿方法

blades.jsの大きな特徴のひとつが、クラスのカプセル化、つまりメンバの隠匿を(限定的にではあるが)実現している点です。
具体的に書くと、

  • プロパティ(インスタンスプロパティ)は全てprotectedになります。
  • メソッド(インスタンスメソッド)作成時にはそのアクセス指定(public, protected, private)を選ぶことができます。

今回は、そのメカニズムを解説していきます。

JavaScriptではクラスのメンバがすべてpublicになってしまう

まず、以下のような簡単なクラスを考えてみます。

var SomeClass = function(){
    this.someProperty = "hoge";
};

SomeClass.prototype.someMethod = function(){
    alert("foo");
};

var instance = new SomeClass();

alert(instance.someProperty);//hogeが表示される
instance.someMethod();//fooが表示される

上のコードではSomeMethodというメソッドとSomePropertyというプロパティ持ったSomeClassというクラスを作成し、
その後、インスタンスを作成してそのプロパティとメソッドにアクセスしています。
このように、JavaScriptにおいて一般的な方法でクラスを作成した際、そのメンバは事実上publicとなり外から丸見えになってしまいます。
それでは、JavaScriptにおいてクラスのメンバを隠匿する、すなわちクラスのカプセル化を実現する方法はないのでしょうか?

まずはすべてを隠す

まず、上で作成したクラスを少し拡張してみましょう。

var SomeClass = function(){
    this.privateProperty = "private";
    this.publicProperty = "public";
};

SomeClass.prototype.privateMethod = function(){
    alert("private");
};

SomeClass.prototype.publicMethod = function(){
    alert("public");
};

//インスタンスを作成
var instance = new SomeClass();

//プロパティへのアクセス
alert( instance.privateProperty );
alert( instance.publicProperty );

//メソッドへのアクセス
instance.privateMethod();
instance.publicMethod();

先ほど作成したSomeClassクラスに新たに以下のようなプロパティとメソッドを作成しました。

  • private(にしたい)プロパティ:privateProperty
  • public(のままにしたい)プロパティ:publicProperty
  • private(にしたい)メソッド:privateMethod
  • public(のままにしたい)メソッド:publicMethod

これからこのクラスを元に、privateなメンバを隠し、publicなメンバにはアクセスできるよう改造していこうと思います。

とりあえず、privateもpublicも関係なく、メンバ、さらにはインスタンスまで全てを隠してしまいましょう。
JavaScriptで何かを隠すとなると、function(){}の出番ですね。
インスタンスをfunction(){}で覆ってしまいます。

var SomeClass = function(){
    this.privateProperty = "private";
    this.publicProperty = "public";
};

SomeClass.prototype.privateMethod = function(){
    alert("private");
};

SomeClass.prototype.publicMethod = function(){
    alert("public");
};

//インスタンスを隠す
var instance = new function(){
    var instance = new SomeClass();
};

//プロパティへのアクセス
alert( instance.privateProperty );//undefined
alert( instance.publicProperty );//undefined

//メソッドへのアクセス
instance.privateMethod();//エラー
instance.publicMethod();//エラー

こうすると、全てのメンバにアクセスできなくなります。
(*注 function(){}で覆ったものを(無駄に)newしている理由は後述します。)
ここから、publicなメンバへのアクセス手段を作成していきます。

var SomeClass = function(){
    this.privateProperty = "private";
    this.publicProperty = "public";
};

SomeClass.prototype.privateMethod = function(){
    alert("private");
};

SomeClass.prototype.publicMethod = function(){
    alert("public");
};

var instance = new function(){
    var instance = new SomeClass();
   
    return {
        publicProperty : instance.publicProperty,
        publicMethod : function(){
            return instance.publicMethod.apply(instance, arguments);
        }
    };
};

//インスタンスを隠す
alert( instance.privateProperty );//undefined
alert( instance.publicProperty );//"public"と表示

//メソッドへのアクセス
instance.privateMethod();//エラー
instance.publicMethod();//"public"と表示

さきほど、インスタンスとメンバを隠すためにfunction(){}で覆いましたが、その関数がpublicメンバへの参照の一覧を返すように修正しました。
これでpublicなメンバにだけアクセスする事が出来ますね。

さて、これで完成!!!と言いたいところですが、ちょっと注意が必要です。
それはpublicPropertyについてです。

例えば、以下のような操作するとどうでしょうか?

var SomeClass = function(){
    this.privateProperty = "private";
    this.publicProperty = "public";
};

SomeClass.prototype.privateMethod = function(){
    alert("private");
};

SomeClass.prototype.publicMethod = function(){
    alert("public");
};

var instance = new function(){
    var instance = new SomeClass();
   
    return {
        publicProperty : instance.publicProperty,
        publicMethod : function(){
            return instance.publicMethod.apply(instance, arguments);
        }
    };
};

instance.publicPropety = "modified!!";
alert(instance.publicPropety);//"modified!!"と表示はされるけれど・・・

このように代入しても実際のインスタンスのプロパティの値は変更されません。
変更されるのはpublicメンバにアクセスできるようにreturnしたオブジェクトのpublicPropertyなのです。
また、外からの代入はもちろん内部での値の変更も反映されませんね。
この問題を解決するには、以下のような方法が考えられます。

var SomeClass = function(){
    this.privateProperty = "private";
    this.publicProperty = "public";
};

SomeClass.prototype.privateMethod = function(){
    alert("private");
};

SomeClass.prototype.publicMethod = function(){
    alert("public");
};

var instance = new function(){
    var instance = new SomeClass();
   
    return {
        setPublicProperty : function(val){
            instance.publicProperty = val;
        },
        getPublicProperty : function(){
            return instance.publicProperty;
        },
        publicMethod : function(){
            return instance.publicMethod();
        }
    };
};

alert( instance.getPublicProperty() );//"public"と表示される
instance.setPublicProperty("modified!!");
alert( instance.getPublicProperty() );//"modified!!"と表示される

これで外部からのpublicPropertyへの変更も、内部からのpublicPropertyへの変更も問題なくなりました。
このようにpublicなプロパティを作成するには設定&取得用のアクセサメソッドを作成しなければなりません。

最後にこれまでの内容を整理します。

var SomeClass = function(){
    this.privateProperty = "private";
    this.publicProperty = "public";
};

SomeClass.prototype.privateMethod = function(){
    alert("private");
};

SomeClass.prototype.publicMethod = function(){
    alert("public");
};

var PseudoConstructor = function(){
    var instance = new SomeClass();
   
    return {
        setPublicProperty : function(val){
            instance.publicProperty = val;
        },
        getPublicProperty : function(){
            return instance.publicProperty;
        },
        publicMethod : function(){
            return instance.publicMethod();
        }
    };
};

var instance = new PseudoConstructor();

alert( instance.privateProperty );//undefined
alert( instance.publicProperty );//undefined
alert( instance.getPrivateProperty() );//エラー
alert( instance.getPublicProperty() );//"public"と表示

instance.privateMethod();//エラー
instance.publicMethod();//"public"と表示

前回からの変更点はinstanceをラップした関数を一度、PseudoConstructor(疑似コンストラクタ)という名前の変数に代入している点です。
PseudoConstructorは文字通りコンストラクタではないので大文字で始める必要も、newする必要もありません。
しかし、ユーザはそれを知りませんし知る必要もありません。
よってリファレンス等も含め、あえてnewしています。
(ちなみに、new演算子はnewされた関数がオブジェクトを返す場合はそれをそのまま返してくれます。)

blades.jsではこのPseudoConstructorの生成とpublicメンバへのアクセス手段の作成を自動化しており、クラスを作成する際にユーザーはこのような面倒な記述を行う必要はありません。
クラスのカプセル化を実現しつつ、インスタンスは通常の方法のようにnewで生成できる、これがblades.jsの大きなポイントの一つです

最後に

さて、今回はJavaScriptにおけるクラスのカプセル化について考察しました。

次回はクラスの継承方法について検討したいと思います。



Comments(0)

お気軽にコメントまたは評価を投稿してください。

お名前とE-mailアドレスをご入力の上、コメント本文または評価を入力してください。※E-mailアドレスは掲載されません。

Trackbacks(0)

お気軽にトラックバックしてください。


About Blades LAB

知的好奇心探究サイト 先端研究ブレイドLAB

先端研究ブレイドLABは、株式会社ブレイドが自社で開発したサービス・製品について紹介するサイトです。社員が興味を持った技術を駆使してサービス・製品へと昇華し、広く世間に対して公表することで、技術ノウハウを共有することを目指します。

また、2009年度まで運営していたMT/CMSブログを統合し、WEBに関する最先端の技術トピックスも紹介しております。


Project Blog!

本記事の連載は終了いたしました。

プロジェクトブログ

Blades.co.,ltd. WEB CM

ムービーを再生する

株式会社ブレイド設立2周年を記念して制作したWEB限定CMです。Adobe After Effects CS3で制作しました。
※CM中の企業ロゴは旧ロゴです。

ムービーを再生する