prototype.js でデザインパターン - Factory Method

お次は「インスタンス作成をサブクラスにまかせる」Factory Method パターン。これまた定番。

var Main = Class.create();
Main.prototype = {
    initialize : function() {},
    main : function() {
        var factory = new IDCardFactory();
        var cards = new Array(
            factory.create('Erich Gamma'),
            factory.create('Richard Helm'),
            factory.create('Ralph Johnson'),
            factory.create('John lissides')
        );
        for (var i = 0; i < cards.length; i++) {
            cards[i].use();
        }
    }
}

というクライアントがあります。

クライアントで利用する主人公であるところのインスタンスの生成ロジックを Factory の中に抽象化してやって、具体的な new によるインスタンス生成の束縛からクラスを解放してやりましょう、ということになります。デザパタ本では Factory の中でインスタンスを生成、そのインスタンスを Factory に登録、といった感じでインスタンス生成に関連した別の処理を Factory の中に一緒に抽象化するってことをやってます。

さて、まずは Factory の抽象クラスです。Template Method の時に同じく Abstract.Factory とします。

Abstract.Factory = function() {};
Abstract.Factory.prototype = {
    create : function (owner) {
        var p = this.createProduct(owner);
        this.registerProduct(p);
        return p;
    },
    createProduct : function (owner) {},
    registerProduct : function(product) {},
}

create の中身が Template Method になってますね。これがインスタンス生成に関する処理を一緒に抽象化する、ということなんでしょう。

Concrete Factory を作るまえに、実際に生成される具体的なインスタンスであるところの IDCard を先に作ってしまいましょう。

var IDCard = Class.create();
IDCard.prototype = {
    initialize : function(owner) {
        document.writeln(owner + 'のカードを作ります。<br>');
        this.owner = owner;
    },
    use : function() {
        document.writeln(this.owner + 'のカードを使います。<br>');
    }
}

クライアントでこの IDCard という記述が一切でてこない、というところが Factory Method の肝になります。

お待ちかね、Concrete Factory は以下です。

var IDCardFactory = Class.create();
IDCardFactory.prototype = (new Abstract.Factory).extend({
    initialize : function() {
        this.owners = new Array();
    },
    createProduct : function(owner) {
        return new IDCard(owner);
    },
    registerProduct : function(product) {
        this.owners.push(product);
    },
});

デザパタ本では JavaArrayList でデータ構造を持ってますが、JavaScript の Array は ArrayList に同じく可変長なので、それで代用。

実行結果は、

Erich Gammaのカードを作ります。
Richard Helmのカードを作ります。
Ralph Johnsonのカードを作ります。
John lissidesのカードを作ります。
Erich Gammaのカードを使います。
Richard Helmのカードを使います。
Ralph Johnsonのカードを使います。
John lissidesのカードを使います

となります。