Noncommutative Field

記事検索

<<ECMAScript で Singleton パターンを使う | ECMAScript で変数やメソッドを隠蔽する方法 | prototype.js を用いた OOP で、後方参照できるクラスを宣言する>>

2005年08月31日

ECMAScript で変数やメソッドを隠蔽する方法

指向性メモ::2005-07-24::クロージャベース・オブジェクト指向による JavaScript の記事にあったものを改良し、汎用的に使えるようにしてみた。

Object.prototype.createChild = function(source){
    function Temp(){};
    Temp.prototype = this;
    var childObject = new Temp;
    if(source instanceof Object){
        childObject.extend(source);
    }
    return childObject;
}

Object.prototype.extend = function(source , deeply){
    if(!(source instanceof Object)){
        throw new Error;
    }
    for(var property in source){
        if((deeply || source.hasOwnProperty(property)) && this[property] !== source[property]){
            this[property] = source[property];
        }
    }
    return this;
}


Function.prototype.inherit = function(superclass){
    function Temp(){};
    Temp.prototype = superclass.prototype;
    return this.prototype = new Temp;
};


var Class = (function(){
    function createSubclass(closure){
        if(!(closure instanceof Function)){
            throw new Error;
        }
        var newClass = function(){
            var toInherit = (this == newClass);
            var constructor;
            if(toInherit){
                constructor = arguments[0];
            }else{
                constructor = newClass;
            }
            var superclass = newClass.prototype.constructor;
            var inheritance = superclass.call(superclass , constructor);
            var published = inheritance.published;
            closure.call(published , inheritance.privileged);
            if(toInherit){
                return inheritance;
            }else{
                published.constructor = newClass;
                if(published.initialize !== undefined){
                    published.initialize.apply(published , arguments);
                }
                return published;
            }
        }
        newClass.inherit(this);
        newClass.createSubclass = createSubclass;
        return newClass;
    }
    return {
        create : function(closure){
            if(!(closure instanceof Function)){
                throw new Error;
            }
            var newClass = function(){
                var toInherit = (this == newClass);
                var constructor;
                if(toInherit){
                    constructor = arguments[0];
                }else{
                    constructor = newClass;
                }
                var privileged = {};
                var published = constructor.prototype.createChild();
                closure.call(published ,privileged);
                if(toInherit){
                    return {privileged : privileged , published : published};
                }else{
                    if(published.initialize !== undefined){
                        published.initialize.apply(published , arguments);
                    }
                    return published;
                }
            }
            newClass.createSubclass = createSubclass;
            return newClass;
        }
    }
})();

利用法は以下の通り。

var someClass = Class.create(function(privileged){
    var privateVariable = ... ;
    var privateMethod = function(){...};
    privileged.protectedVariable = ... ;
    privileged.protectedMethod = function(){...};
    this.publicVarible = ... ;
    this.publicMethod = function(){...};
});
someClass.prototype.publicProperty = ... ;

var Subclass = someClass.createSubclass(function(privileged){
    //スーパークラスのメソッドを呼び出す場合はこうする
    var superProtectedMethod = privileged.protectedMethod;
    privileged.protectedMethod = function(){
        superProtectedMethod();
        ... ;
    };
    ... ;
});

var instance = new someClass;

クラスやサブクラスを作る場合は、Class.createsomeClass.createSubclass の引数として Function オブジェクトを渡す。渡した関数内で宣言された変数が private になり、渡す関数の第一引数のプロパティが protected になる。this は通常のやり方でコンストラクタを使ったときと同じく生成されるオブジェクトそのものを表し、プロパティは public になる。また、通常のやり方にそって someClass.prototype にプロパティを追加することもできる。ただし、この場合は private や protected な変数を使うことはできない。

また、instanceofinstance.constructor も通常通り使える。

var someClass = Class.create(function(){});
var Subclass = someClass.createSubclass(function(){});
var instance = new Subclass;
//printは適当に定義
print(instance.constructor == Subclass);//true
print(instance instanceof Subclass);//true
print(instance instanceof someClass);//true

使用例

var Product = Class.create(function(){
    this.extend({
        initialize : null,
        getID : null
    });
});

var Factory = Class.create(function(privileged){
    this.extend({
        initialize : null,
        create : function(id){
            var product = privileged.createProduct(id);
            privileged.registerProduct(product);
            return product;
        }
    });
    privileged.extend({
        createProduct : null,
        registerProduct : null
    });
});


var ConcreteProduct = Product.createSubclass(function(){
    var id;
    this.extend({
        initialize : function(_id){
            id = _id;
        },
        getID : function(){
            return id;
        }
    });
});

var ConcreteFactory = Factory.createSubclass(function(privileged){
    var IDList = [];
    this.extend({
        initialize : undefined,
        each : function(func){
            for(var i = 0; i < IDList.length; i++){
                func(IDList[i]);
            }
        }
    });
    privileged.extend({
        createProduct : function(id){
            return new ConcreteProduct(id);
        },
        registerProduct : function(product){
            IDList.push(product);
        }
    });
});


var factory = new ConcreteFactory;
factory.create('001');
factory.create('002');
factory.create('003');
//print()は処理系に合わせて定義してください。
factory.each(function(x){print(x.getID());});
/*
001
002
003
*/

注意点

デメリット

参考リンク

指向性メモ::2005-07-24::クロージャベース・オブジェクト指向による JavaScript

別のやり方で変数の隠蔽を実現しているところ
Function.prototype.inherit の元ネタ

Starry Night - Diary - 2001/12/20 JavaScript 継承3

this について

fladdict.net blog: JavaScript, ActionScriptにおける .this とは何なのか?


継承されたか、new されたかの判別方法が手抜きなので直さねばと思っているが、やる気が起きない。

posted by malt3rd at 13:05 | TrackBack(0) | ECMAScript

この記事へのトラックバック

プロフィール

名前
malt3rd
URL
http://www.geocities.jp/commutativefield/
メールアドレス
malt3rd {dot} NoncommutativeField {at} submit-asap {dot} org
Powered by Seesaa

RDF Site Summary

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。