C++ による実装例だ。幾通りか見てみよう。「メンバ関数ポインタをリクエストに使う」と「メンバ関数テンプレートを使う」は、メールでソースコードをいただきました。ありがとうございました。
まずは、もっとも単純な例を。リクエストを整数値で与えて、それを判定してハンドラを呼び出す機構だ。ここでは、リクエストを処理するための共通クラス Handler を利用してみた。チェインの中のクラスは、Handler クラスを継承して、必要なハンドラをオーバーライドして使うんだ。
|
リクエスト定数 |
Handler クラス |
|
Button クラス |
Dialog クラス |
|
Application クラス |
main() |
とりあえず、これで望み通りの動作はする。しかしこの実装だと、ほかの言語と比べると、欠点が多いんだよね。
なんでこんなことになってしまうかというと、原因は 2 つ。1 つは、C++ ではメンバ関数の有無が調べられない。だから、勢いすべてのメンバ関数を実装、なんてことになってしまう。もう 1 つは、自分の知らない関数を呼び出すことができないこと。このため、Handler クラスはすべてのハンドラをあらかじめ知っておかなくてはならない。
(実装ファイル:memberFuncPointer.cpp)
ということで、次はメンバ関数ポインタを使ってみた例だ。リクエストとしてメンバ関数をポインタを使う。こんな風に。
|
Handler クラス |
リクエスト定数 |
このように、Handler クラスのハンドラのメンバ関数ポインタを、リクエストとして使うんだ。これを使うと、リクエストとハンドラを結びつける handlerRequest() は、次のようになる。ただしこの方法だと、それぞれのデフォルトハンドラを実装する必要が出てきてしまうんだ。その実装の中でチェインをたぐる動作をすることになる。たとえば、デフォルトの help ハンドラは次のような感じ。
|
Handler::handleRequest() |
Handler::help() |
完全なソースコードは、ファイルを参照してくれ。この実装だと、さっきの問題点のうち「リクエストとハンドラの対応付けを、コードの中でやらなくてはいけない」という点は解消される。ただし、
という、新たな問題点が生じることになってしまう。さっきはリクエストを数値で与えたけど、今度はメンバ関数ポインタだからね。
(実装ファイル:memberFuncTemplate.cpp)
じゃあ、これはどうよ、ってんで出てきたのが、次の実装。これは、動的にリクエストとハンドラを増やすことと、同じ機能を実現することを目的に書かれたものだ。どうするかっていうと、handleRequest をあらゆるリクエストを受け付ける、メンバ関数テンプレートとして定義する。そして、渡されたタイプによって、呼び分ける handlerRequestHelper() っていう関数を作るんだ。この中で、ハンドラを検索する。
|
Handler::handleRquest(), Handler::handerRequestHelper() |
あらたにハンドラを登録するには、次のマクロを使う。
|
リクエスト特性定義用マクロ |
詳細は実装のファイルを見てくれ。正直な話、「よくやったな〜」という気持ちと、「ここまでやるか?」という気持ちが半々だ。こうなると、これが C++ の言語特性を生かした実装か、と言われてもちょっと微妙な感じも。
いろいろ見てきたけど、生粋の C++ 使いの人には不満を感じるところもあるかもしれない。「問題、問題っていうけど、そもそも動的なリクエストのハンドラの追加って意味あるの?」とか。まぁ、ちょっと待って。ここでの目的は、Chain of Responsibility の実装の議論ではなく、これを題材にした各言語の比較なので。他の言語での実装と見比べて、C++ の特徴を感じてください。