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