mkinoさんによる オブジェクト指向の言語比較論 中の、 Chain of Responsibility の C++ による実装 を自分なりに書き直してみました。
記事のオリジナルの実装が批判されている点は、次の通り。
まず、メソッドでないもの(例えば文字列)を通して何らかのメソッドを得るためには、 C++では、絶対どこかに変換を行うコードが必要になってしまいます。 なので、2番目は無理。あきらめます。^^; 残りの二つを何とかしましょう。
以下の実装では、
となっています。
// チェインのための Handler クラス
class Handler
{
private:
// 次の handler
Handler* successor_;
// 'リクエスト'
class Request {
friend class Handler;
virtual void sendTo( Handler* p ) const = 0;
virtual bool sendableTo( const Handler* p ) const = 0;
};
public:
template <class Impl>
class RequestGen : public Request {
void sendTo( Handler* p ) const
{ dynamic_cast<Impl*>(p)->invoke(); }
bool sendableTo( const Handler* p ) const
{ return dynamic_cast<const Impl*>(p)!=0; }
};
// コンストラクタ
Handler( Handler* successor )
: successor_(successor) {}
// デストラクタ
virtual ~Handler() {}
// リクエストが受付可能かどうかのチェック
bool hasHandler( const Request& req ) const
{ return req.sendableTo(this); }
// リクエストを処理する
void handleRequest( const Request& req )
{
if( req.sendableTo(this) ) {
req.sendTo(this);
}
else if( successor_ ) {
successor_->handleRequest(req);
}
}
};
// リクエスト 'HELP'
struct HelpHandler {
void invoke() { help(); }
virtual void help() = 0;
};
static Handler::RequestGen<HelpHandler> HELP_REQUEST;
// リクエスト 'PRINT'
struct PrintHandler {
void invoke() { print(); }
virtual void print() = 0;
};
static Handler::RequestGen<PrintHandler> PRINT_REQUEST;
// リクエスト 'PREVIEW'
struct PreviewHandler {
void invoke() { preview(); }
virtual void preview() = 0;
};
static Handler::RequestGen<PreviewHandler> PREVIEW_REQUEST;
// Button クラス
class Button : public Handler, public HelpHandler
{
public:
Button( Handler* suc ) : Handler(suc) {}
void help() {
cout << "ButtonHelp" << endl;
}
};
// Application クラス
class Application : public Handler, public PreviewHandler
{
public:
Application( Handler* suc ) : Handler(suc) {}
void preview() {
cout << "AppPreview" << endl;
}
};
// Dialog クラス
class Dialog : public Handler, public PrintHandler
{
public:
Dialog( Handler* suc ) : Handler(suc) {}
void print() {
cout << "DialogPrint" << endl;
}
};
int main()
{
Application app(0);
Dialog dialog(&app);
Button button(&dialog);
button.handleRequest(HELP_REQUEST);
button.handleRequest(PRINT_REQUEST);
button.handleRequest(PREVIEW_REQUEST);
}
public PrintHandler
などが必要なのは面倒だし
error-proneだ。