行为型模式--职责链

1、意图

  使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

2、结构

 

 3、参与者

  Handler:定义一个处理请求的接口。实现后继链(可选)。

  ConcreteHandler:处理它所负责的请求;可访问它的后继者;如果可处理改请求,就处理之,否则将该请求转发给它的后继者。

  Client:向链上的具体处理者对象(ConcreteHandler)提交请求。

4、适用性

  在以下条件下使用职责链(Responsibility):

  有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

  想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

  可处理一个请求的对象集合应被动态指定。

5、代码示例

// HelpHandler类定义了处理帮助请求的接口。它维护一个帮助主题(缺省值为空),
// 并保持对帮助处理对象链中它的后继者的引用。关键的操作是HandleHelp,它可被子类重定义。
// HasHelp是一个辅助操作,用于检查是否有一个相关的帮助主题。
typedef int Topic; 
const Topic NO_HELP_TOPIC = -1; 
class HelpHandler
{
public:
    HelpHandler(HelpHandler* = 0,Topic = NO_HELP_TOPIC); 
    virtual bool HasHelp(); 
    virtual void SetHandler(HelpHandler*, Topic); 
    virtual void HandleHelp(); 
private:
    HelpHandler* _successor; 
    Topic _topic;
};

HelpHandler::HelpHandler(HelpHandler* h, Topic t) :_successor(h),_topic(t){}
bool HelpHandler::HasHelp ()
{
    return _topic != NO_HELP_TOPIC;
}
void HelpHandler::HandleHelp ()
{
    if (_successor != 0)
    {
        _successor->HandleHelp();
    }
}
// 所有的窗口组件都是Widget抽象类的子类。Widget是HelpHandler的子类,
// 因为所有的用户界面元素都可有相关的帮助。(我们也可以使用另一种基于混入类的实现方式)
class Widget : public HelpHandler
{
protected:
    widget(Widget* parent, Topic t = NO_HELP_TOPIC); 
private:
    Widget* _parent;
};

widget::widget (widget* w, Topic t) : HelpHandler(w, t)
{
    _parent = w;
}
// 在我们的例子中,按钮是链上的第一个处理者。Button类是widget类的子类。
// Button构造函数有两个参数:对包含它的窗口组件的引用和其自身的帮助主题。
class Button : public widget
{
public:
    Button(Widget* d, Topic t = NO_HELP_TOPIC); 
    virtual void HandleHelp();
    //Widget operations that Button overrides...
};
// Button版本的HandleHelp的首先测试检查其自身是否有帮助主题。如果开发者没有定义一个帮助主题,
// 就用HelpHandler中的HandleHelp操作将该请求转发给它的后继者。如果有帮助主题,那么就显示它,并且搜索结束。
Button::Button (widget* h, Topic t):Widget(h,t){}
void Button::HandleHelp ()
{
    if (HasHelp())
    {
        // offer help on the button
    }
    else
    {
        HelpHandler::HandleHelp(); 
    }
}
// Dialog实现了一个类似的策略,只不过它的后继者不是一个窗口组件而是任意的帮助请求处理对象。
// 在我们的应用中这个后继者将是Application的一个实例。
class Dialog : public widget
{
public:
    Dialog(HelpHandler* h, Topic t = NO_HELP_TOPIC); 
    virtual void HandleHelp();
    // Widgeet operations that Dialog overrides...
};

Dialog::Dialog (HelpHandler* h,Topic t):Widget(0)
{
    SetHandler(h,t);
}

void Dialog::HandleHelp ()
{
    if(HasHelp())
    {
        // offer help on the dialog 
    }
    else
    { 
        HelpHandler:HandleHelp();
    }
}
// 在链的末端是Application的一个实例。该应用不是一个窗口组件,因此Application不是HelpHandler的直接子类。
// 当一个帮助请求传递到这一层时,该应用可提供关于该应用的一般性的信息,或者它可以提供一系列不同的帮助主题。
class Application : public HelpHandler
{
public:
    Application(Topic t):HelpHandler(0,t){}
    
    virtual void HandleHelp();    
    // application-specific operations...
};

void Application::HandleHelp ()
{
    // show a list of help topics
}
// 下面的代码创建并连接这些对象。此处的对话框涉及打印,因此这些对象被赋给与打印相关的主题。
const Topic PRINT_TOPIC = 1; 
const Topic PAPER_ORIENTATION_TOPIC = 2; 
const Topic APPLICATION_TOPIC = 3; 

Application* application = new Application(APPLICATION_TOPIC); 
Dialog* dialog = new Dialog(application, PRINT_TOPIC);
Button* button = new Button(dialog, PAPER_ORIENTATION_TOPIC);

// 我们可对链上的任意对象调用HandleHelp以触发相应的帮助请求。要从按钮对象开始搜索,只需对它调用HandleHelp
// 在这种情况下,按钮会立即处理该请求。注意任何HelpHandler类都可作为Dialog的后继者。
// 此外,它的后继者可以被动态地改变。因此不管对话框被用在何处,你都可以得到它正确的与上下文相关的帮助信息。
button->HandleHelp();

6、总结

  职责链模式降低了对象之间的耦合程度,请求的发送方并不知道最终处理请求的是哪一个对象,它只需指派自己的后继处理者,由后继者去进行交付或处理请求。

  职责链模式可以灵活地指派请求,当前请求的接收者的后继者可以被动态地指派和调整。

  职责链的链式结构由客户建立和维护,客户负责指定后继者,而链式结构中的对象负责处理(也可以丢弃该请求不处理,那么该请求将在此终止)或继续传递请求。

posted @ 2022-05-03 20:35  流翎  阅读(32)  评论(0)    收藏  举报