Strategy Pattern ~ mixin を利用した

Command Pattern の話の続きです。

最初にこのコマンド処理の仕組みを作った時、私はジョブ処理の方式として「キュー」を用いました。人の行列のイメージですね。最も長く待ったものが最初に処理されるという方式です。

これでしばらくは満足だったんですが、ちょっと複雑なタスクを新たに組み入れた時、スタックも欲しいなと思うようになりました。

キューやスタックというのは要は配列で、要素を追加する方向 (push or unshift) を固定しておけば、取得の方法 (pop or shift) を使い分けるだけで実装できます。つまり、

Command.Center.Manager:

    get_command: function ()
    {
	return this.list.shift();
    }

このメソッドの中身を切り替えるだけで済むわけです。

このような場合に使えるパターンが Strategy Pattern です。同種のアルゴリズムをクラス化してクラスのファミリーを作り、状況に応じてどのインスタンスを利用するか判断する、というもので、抽象クラスや interface を利用して実装するのが一般的だと思います。

今回は、非常に単純なケースですので、クラスを作らない変則的なやり方で解決してみたいと思います。

Mixin です (何度も推すようですが私の prototype.js の拡張を使います)。

個々の strategy をメソッドとして定義するオブジェクト (Ruby 的に言うとモジュール) を作り、それを strategy を必要とする箇所でインクルードする、という方法です。このようになりました:

Command.Center.Manager:

    add_command: function (comd, start)
    {
	if (!comd.kind_of(Command.Abstract))
	    return;

	if (this.list.empty())	// ココ
	    this.include(Command.Center.Manager[comd.prefer_stack() ? 'S'
								    : 'Q']);
	this.list.push(comd);

	if (start)
	    this.start();
    },

(これで id:reinyannyan:20060206:p1 と話が繋がりましたね)

Strategies:

Command.Center.Manager.Q =
{
    get_command: function ()
    {
	return this.list.shift();
    }
};
Command.Center.Manager.S =
{
    get_command: function ()
    {
	return this.list.pop();
    }
};

(一般にスタックは shift/unshift のペアで実装すると思いますが、最初に push を使っていましたのでこうなります。あと、Command.Center.Manager 内では get_command メソッドは空関数にしておけば良いと思います)

初めてコマンドを受け付けた時に、そのコマンド・オブジェクトがスタックとして処理されることを希望すれば Command.Center.Manager.S をインクルードし、そうでなければ .Q をインクルードします。

なお、このように必要な時に一度だけアルゴリズムを選択するパターンを Strategy、何度も切り替えるパターンを State と呼ぶという違いがあるようです。

参考文献:
http://home.earthlink.net/~huston2/dp/strategy.html
http://www.dofactory.com/Patterns/PatternStrategy.aspx