Noncommutative Field

記事検索

<<2005年07月 | 2005年08月 | 2005年09月>>

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

2005年08月13日

ECMAScript で Singleton パターンを使う

石川さんの記事のコメントに解決法があるが、別の解決法を考えてみた。そもそも new が出来ないクラスを作る、というもの。石川さんの記事と同じ例で記述するとこんな感じ。

var Earth = {
    instance : null,
    getInstance : function(){
        if(this.instance === null){
            function Temp(){};
            Temp.prototype = {
                population : 2,
                increasePopulation : function(){
                    this.population *= 2;
                },
                getPopulation : function(){
                    return this.population;
                }
            }
            this.instance = new Temp;
        }
        return this.instance;
    }
}
//new Earth //この行を実行するとエラー

//使い方
var e1 = Earth.getInstance();
var e2 = Earth.getInstance();//e1 == e2
e1.getPopulation();//2
e2.increasePopulation();
e2.getPopulation();//4
e1.getPopulation();//4
問題点
標準的な方法でつくったクラスやオブジェクトと若干挙動が異なる。(e1.constructor != Earth , e1.[[Prototype]] != Earth.prototype)
new Earth は出来ないが、Earth.getInstance.apply({instance : null}) は出来る。

難しく考えすぎていた。prototype.js を使うという前提で、

var Singleton = Class.create();
(function(){
    Singleton.prototype.initialize = function(){};
    var singleton = new Singleton;
    Singleton.prototype.initialize = null;
    Singleton.getInstance = function(){
        return singleton;
    };
})();

var a = Singleton.getInstance();
var b = Singleton.getInstance();
//print()は適当に定義
print(a == b);//true
//var c = new Singleton;
//上の行を実行するとエラー

上記のようにすればよい。

posted by malt3rd at 22:59 | TrackBack(1) | ECMAScript

2005年08月10日

Io っぽい ECMAScript の OOP

//クラスの宣言、コンストラクタ、インスタンス変数
function Class(){
    this.instanceVariable = initialValue;
}
//メソッド
Class.prototype.method = function(){
    ...
}
//サブクラスの宣言、コンストラクタ、継承
function SubClass(){
    ...
}
SubClass.prototype = new Class;
//インスタンスの生成
var object1 = new Class;
var object2 = new SubClass;

通例 ECMAScript では上記のように OOP するのだが、別の流儀を考えてみた。

具体的にいうと、上記のコードが下記のように置き換わる。

/*
新たなオブジェクトを生成するメソッド。
できたオブジェクトのプロトタイプは
メソッドを呼び出したオブジェクト。
*/
Object.prototype.clone = function(){
    var tmpFunction = new Function;
    tmpFunction.prototype = this;
    return new tmpFunction;
}

var Class = new Object;
Class.instanceVariable = initialValue;
Class.method = function(){
    ...
}
var SubClass = Class.clone();
SubClass.... = ...
var object1 = Class.clone();
var object2 = SubClass.clone();

オブジェクトを clone すると、新しいオブジェクトができて、後者は前者のプロトタイプになる。これによりプロトタイプチェーンが直感的に把握しやすくなり、積極的に利用できるようになる。最初の書き方では、コードから直接親子関係を読み取れるわけではない。

object2.[[Prototype]] == SubClass.prototype
object2.[[Prototype]].[[Prototype]] == SubClass.prototype.[[Prototype]] == Class.prototype

後の書き方では、clone した/された関係がそのままプロトタイプチェーン上の親子関係になる。

object2.[[Prototype]] == SubClass
object2.[[Prototype]].[[Prototype]] == SubClass.[[Prototype]] == Class

Io のオブジェクトの仕組みがこれに似ているらしい(筆者は Io に触れたことは無いのでとんちんかんなことをいっているかもしれない)。

この流儀のメリットは、シンプルであることだ。ただそれに意味あるのかといわれると、まだ積極的に活用していないので何ともいえない。

posted by malt3rd at 20:36 | TrackBack(0) | ECMAScript

2005年08月02日

再開に関する備忘録

以前日記(Commutative Field)を公開していたが、自分で消した。理由は使っていたサービスの規約に違反していたことに気づき、殆ど実害のないものだったがどうしても気になったからだ。直すのも面倒。要するに飽きた。

それから長らく、有料のサーバを借りて再開しようと常々思っていた。やっぱり色々いじれるほうがよい。しかし、面倒くさい・忙しい等の理由で進まなかった。

以上の事はともかく、書きたいことが幾つか浮かんできたので、放置していた Seesaa のアカウントを再利用することを決めた。

posted by malt3rd at 14:31 | TrackBack(0) | 雑談

プロフィール

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

RDF Site Summary

×

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