ねらい
- リクエストの送信者と受信者との密結合をほぐす
- 1つ以上のオブジェクトにリクエストの処理の機会を与える
モチベーション
- 例) コンテキスト依存のヘルプ機能
- UI上の任意の場所をクリックして、ヘルプを表示できるとする
- ダイアログの中のボタンをクリックしたとする
- ヘルプリクエストが送信される
- ボタンにヘルプがあれば、ボタンについてのヘルプ表示
- さもなくば、ボタンを含むダイアログについてのヘルプを表示
- それもなければ、ダイアログを含むアプリケーションについてのヘルプを表示
- ヘルプ表示リクエストがどのオブジェクトに受信されて処理されるかを明示的に決めない
- リクエスト送信者と受信者とが疎結合化される
つかいどころ
- 1つ以上のオブジェクトがリクエストを処理するかもしれない
- 受信者を明示的に決めずにリクエストを送信したい
- リクエストを受信して処理するオブジェクトを動的に決めたい
登場人物
Handler
- リクエストを処理するためのインタフェースを定義する
- (オプショナル)「次のリクエスト処理候補」のリンクを定義する
- ゲッタを用意しなくても、protectedメンバ変数でもいい
- Compositeパターン等ですでにチェーンができていればそれを使っていい
ConcreteHandler
- 処理する責務のあるリクエストを受信し処理する
- 「次の人」(successor)にアクセスできる
- そうでないリクエストはsuccessorに丸投げする
- Client
クライアントコードからの利用
- Clientがリクエストを送信し、
ConcreteHandler
が処理するまでリクエストは伝播する
結果
- オブジェクト間の結合が弱まる
- リクエストの送信者や、リクエストを処理せずにsuccessorに丸投げするオブジェクトは、どのオブジェクトがリクエストを処理するかを知らずに済む
- 「良しなに」処理されるであろう、ということだけを知っている
- 結果、オブジェクト間の相互の結びつきが簡潔になる
- 可能性のあるすべての受信者、ではなく、successorの参照1つだけ持っていればよい
- オブジェクトに責務を割り振るうえで、柔軟性が高まる
- 実行時にsuccessorのチェーンを変えたり追加したりすることで、リクエストの処理の責務を柔軟に変えられる
- リクエストを誰かが受信・処理してくれる、という保証はない
- ヘルプの例では、アプリケーションもリクエストをスルーしたら誰も処理しないことになる
- successorのチェーンが途中で途切れていても同様
実装にあたり考えるべきこと
- successorのチェーンの実装
Handler
に参照を新規追加する
- 既存の参照を使う
- Compositeパターンを使用している場合など
- 空間の節約になる
- 既存のオブジェクト構造と、successorのチェーンの構造とが一致しない場合、冗長なチェーンができることがある
- ヘルプの例では、ボタンがlegendつきのfieldsetの中にある場合など
ダイアログ <>-- fieldset <>-- ボタン
という構造
- fieldsetにリクエストを送るのは冗長
- successorにリクエストを丸投げする処理
Handler::HandleRequest(Request* request)
のデフォルト実装はthis->_successor->HandleRequest(request)
とし、適宜overrideする
- リクエストの表現
Handler::HandleHelp()
というようにハードコーディングする
Request
オブジェクト等は用意しない。メソッド呼び出しそのものがリクエスト
- 柔軟性に欠ける
Handler::HandleRequest(int request_code)
というように、パラメータを受け取る
Handler::HandleRequest(Request* request)
というように、リクエストをオブジェクトにする
- GoF本では、型フィールドでswitch-caseするやり方が提示されている
Handler
に一塊の引数を渡せる
enum RequestKind {
REQUEST_NULL,
REQUEST_HELP,
};
void Handler::HandleRequest (Request* theRequest) {
switch(theRequest->GetKind()) {
case REQUEST_HELP:
HandleHelp((HelpRequest*) theRequest);
break;
}
}
RequestKind Request::GetKind() {
return REQUEST_NULL;
}
RequestKind HelpRequest::GetKind() {
return REQUEST_HELP;
}
void Handler::HandleRequest (Request* theRequest) {
theRequest->HandledBy(this);
}
void Request::HandledBy(Handler* handler) {
}
void HelpRequest::HandledBy(Handler* handler) {
handler->HandleHelp(this);
}
関連するパターン
- Composite
- 併用されること多し
- 親ノードがsuccessorになる