CompositeパターンとCommandパターンの事例
要求に対する処理をコマンド化する
Compositeパターンを適用したことでツリー構造を管理するComposite役と要求に対する処理の役割を持つLeaf役に分けて考えることができるようになりました。しかし、これで汎用的なフレームワークが完成したとはいえません。なぜならComponent役に、Composite役にのみ必要なメソッドが含まれているからです。このままではLeaf役にComposite役に関係のないメソッドを実装する可能性が残ります。
Component役のメソッドを整理すると要求を出すrequestメソッド以外はすべてComposite役のメソッドです。そこで図3-1のように要求を出すrequestメソッドとそれ以外のメソッドを分けてインターフェースを再定義します。さらに「要求を出す」部分にCommandパターンを適用します。
ちなみに、Chain of Responsibilityパターンも「要求に対する処理」をインスタンス化してチェーン構造にします。処理の呼び出しをインスタンス化するという、その考え方はCommandパターンに通じるものがあります。
Chain of ResponsibilityパターンにCompositeパターンとCommandパターンを適用したことで「ツリー構造」と「要求に対する処理」をうまく切り離すことができました。
フレームワークの利用者は【利用例4-A】のように「要求に対する処理」の実現に集中してプログラミングができるようになります。また【利用例4-A】の(1)(2)のように複数の要求に対する処理(コマンド)を組み合わせて新しいコマンドを作ることができます。
【利用例4-A】
public class TelSupportContext extends Context {
// 要求の内容を表すパラメータを構成する
}
:
public class InitStandardSupport implements Command {
public boolean request(Context context) { … } // 一般サポート初回
}
:
public class StandardSupport implements Command {
public boolean request(Context context) { … } // 一般サポート継続
}
:
public class InitSpecialSupport implements Command {
public boolean request(Context context) { … } // 特別サポート初回
}
:
public class SpecialSupport implements Command {
public boolean request(Context context) { … } // 特別サポート継続
}
:
// 一般サポート ……(1)
CompositeCommand standardSupport = new ConcreateCompositeCommand();
standardSupport.add(new InitStandardSupport());
standardSupport.add(new StandardSupport());
// 特別サポート ……(2)
CompositeCommand specialSupport = new ConcreateCompositeCommand();
specialSupport.add(new InitSpecialSupport());
specialSupport.add(new SpecialSupport());
// 電話サポート
CompositeCommand telSupport = new ConcreateCompositeCommand();
telSupport.add(standardSupport);
telSupport.add(specialSupport);
// 要求を投げる
Context ctx = new TelSupportContext(); // 問い合わせ内容
telSupport.request(ctx);
:
■commons chainに関する補足
commons chainは処理のチェーンをCompositeパターンで形成し、要求に対する処理の実行をCommandパターンで実現しています(図3-2)。
Chainインターフェースを実装したChainBaseクラスはCompositeパターンのComposite役であり、ツリー構造を実現しています。
CommandインターフェースはCommandパターンのCommand役であり、要求を出すための共通APIの役割を持ちます。ChainBaseクラスはその共通APIから要求を受けると自身で管理しているCommandインターフェースを持つオブジェクト(要求に対する処理)をすべて実行します。一方、要求に対する処理は、Commandインターフェース、またはFilterインターフェースを直接実装することで実現しています。
Chain of Responsibilityパターンでは要求に対する処理を呼び出す場合、その呼び出しを再帰呼び出しで実現することがあります。再帰呼び出しはメソッドを呼び続けて要求を満たしたとき、呼び出し元に戻っていく性質があります。その戻っていくタイミングで「戻りの処理」を行うことができます。commons chainではCommandインターフェースのかわりにFilterインターフェースを実装することで、再帰呼び出しと同様の「戻りの処理」も行うことができます(図3-3)。
CompositeパターンとCommandパターンは最強コンビ!?
今回紹介したcommons chainは個人的にCommandパターンのフレームワークのように感じましたが、Filterインターフェースを実装することで再帰呼び出しの「戻りの処理」を実現できるところは作者たちのChain of Responsibilityパターンへのこだわりを感じます。ちなみにcommons chain はstruts 1.3系の標準リクエストプロセッサ「ComposableRequestProcessor」でHTTPリクエストを処理するために利用されています。
このほかにも、CompositeパターンとCommandパターンの組み合わせは、複数のデータベースの更新を1つのトランザクションとして実行する場合など、いくつかの処理をまとめて実行するような仕組みを作る場合によく利用します。
次回はいよいよ最終回です。最終回にふさわしい事例を紹介します。それではお楽しみに!
【参考文献】
Erich Gamma, Rechard Helm, Ralph Jonson, John Vlissides『オブジェクト指向における再利用のためのデザインパターン』ソフトバンククリエイティブ(発行年:1999)
結城 浩『Java言語で学ぶデザインパターン入門』ソフトバンクパブリッシング(発行年:2001)
「Apache Commons - commons chain」(http://commons.apache.org/chain/) (アクセス:2009/04)