⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pat5a.htm

📁 四人帮《设计模式》一书英文版本
💻 HTM
📖 第 1 页 / 共 2 页
字号:
forward only the fixed set of requests that the Handler class defines.</P>

<A NAME="auto1049"></A>
<P>An alternative is to use a single handler function that takes a
request code (e.g., an integer constant or a string) as parameter.
This supports an open-ended set of requests. The only requirement is
that the sender and receiver agree on how the request should be
encoded.</P>

<A NAME="auto1050"></A>
<P>This approach is more flexible, but it requires conditional statements
for dispatching the request based on its code.  Moreover, there's no
type-safe way to pass parameters, so they must be packed and unpacked
manually.  Obviously this is less safe than invoking an operation
directly.</P>

<A NAME="auto1051"></A>
<P>To address the parameter-passing problem, we can use separate request
<EM>objects</EM> that bundle request parameters. A <CODE>Request</CODE>
class can represent requests explicitly, and new kinds of requests can
be defined by subclassing. Subclasses can define different parameters.
Handlers must know the kind of request (that is, which
<CODE>Request</CODE> subclass they're using) to access these parameters.</P>

<A NAME="typecheck-runtime"></A>
<P>To identify the request, <CODE>Request</CODE> can define an accessor
function that returns an identifier for the class.  Alternatively, the
receiver can use run-time type information if the implementation
languages supports it.</P>

<A NAME="auto1052"></A>
<P>Here is a sketch of a dispatch function that uses request objects to
identify requests.
A <CODE>GetKind</CODE> operation defined in the base <CODE>Request</CODE>
class identifies the kind of request:</P>

<A NAME="auto1053"></A>
<PRE>
    void Handler::HandleRequest (Request* theRequest) {
        switch (theRequest->GetKind()) {
        case Help:
            // cast argument to appropriate type
            HandleHelp((HelpRequest*) theRequest);
            break;
    
        case Print:
            HandlePrint((PrintRequest*) theRequest);
            // ...
            break;
    
        default:
            // ...
            break;
        }
    }
</PRE>

<A NAME="extnd-hndlr"></A>
<P>Subclasses can extend the dispatch by overriding
<CODE>HandleRequest</CODE>.  The subclass handles only the
requests in which it's interested; other requests are forwarded to the
parent class.  In this way, subclasses effectively extend (rather than
override) the <CODE>HandleRequest</CODE> operation.
For example, here's how an <CODE>ExtendedHandler</CODE> subclass extends
<CODE>Handler</CODE>'s version of <CODE>HandleRequest</CODE>:</P>

<A NAME="auto1054"></A>
<PRE>
    class ExtendedHandler : public Handler {
    public:
        virtual void HandleRequest(Request* theRequest);
        // ...
    };
    
    void ExtendedHandler::HandleRequest (Request* theRequest) {
        switch (theRequest->GetKind()) {
        case Preview:
            // handle the Preview request
            break;
    
        default:
            // let Handler handle other requests
            Handler::HandleRequest(theRequest);
        }
    }
</PRE>

<A NAME="auto1055"></A>
<P></P>

<A NAME="doesnotunder"></A>
<A NAME="forward-req"></A>
<LI><EM>Automatic forwarding in Smalltalk.</EM>
You can use the <CODE>doesNotUnderstand</CODE> mechanism in Smalltalk to
forward requests.  Messages that have no corresponding methods are
trapped in the implementation of <CODE>doesNotUnderstand</CODE>, which
can be overridden to forward the message to an object's successor.
Thus it isn't necessary to implement forwarding manually; the class
handles only the request in which it's interested, and it relies on
<CODE>doesNotUnderstand</CODE> to forward all others.</LI>

</OL>

<A NAME="samplecode"></A>
<H2><A HREF="#knownuses"><IMG SRC="down3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/down3.gif" BORDER=0 ALT="next: 
Known Uses"></A> Sample Code</H2> 

<A NAME="auto1056"></A>
<P>The following example illustrates how a chain of responsibility can
handle requests for an on-line help system like the one described
earlier.  The help request is an explicit operation.  We'll use existing
parent references in the widget hierarchy to propagate requests
between widgets in the chain, and we'll define a reference in the
Handler class to propagate help requests between nonwidgets in the
chain.</P>

<A NAME="auto1057"></A>
<P>The <CODE>HelpHandler</CODE> class defines the interface for handling
help requests. It maintains a help topic (which is empty by default)
and keeps a reference to its successor on the chain of help handlers.
The key operation is <CODE>HandleHelp</CODE>, which subclasses
override. <CODE>HasHelp</CODE> is a convenience operation for checking
whether there is an associated help topic.</P>

<A NAME="auto1058"></A>
<PRE>
    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();
        }
    }
</PRE>

<A NAME="widget-class"></A>
<P>All widgets are subclasses of the <CODE>Widget</CODE> abstract class.
<CODE>Widget</CODE> is a subclass of <CODE>HelpHandler</CODE>, since all
user interface elements can have help associated with them.  (We could
have used a mixin-based implementation just as well.)</P>

<A NAME="auto1059"></A>
<PRE>
    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;
    }
</PRE>

<A NAME="auto1060"></A>
<P>In our example, a button is the first handler on the chain.  The
<CODE>Button</CODE> class is a subclass of <CODE>Widget</CODE>.
The <CODE>Button</CODE> constructor takes two parameters: a reference to
its enclosing widget and the help topic.</P>

<A NAME="auto1061"></A>
<PRE>
    class Button : public Widget {
    public:
        Button(Widget* d, Topic t = NO_HELP_TOPIC);
    
        virtual void HandleHelp();
        // Widget operations that Button overrides...
    };
</PRE>

<A NAME="auto1062"></A>
<P><CODE>Button</CODE>'s version of <CODE>HandleHelp</CODE> first tests to see if
there is a help topic for buttons. If the developer hasn't defined
one, then the request gets forwarded to the successor using the
<CODE>HandleHelp</CODE> operation in <CODE>HelpHandler</CODE>.  If there
<EM>is</EM> a help topic, then the button displays it, and the search
ends.</P>

<A NAME="auto1063"></A>
<PRE>
    Button::Button (Widget* h, Topic t) : Widget(h, t) { }
    
    void Button::HandleHelp () {
        if (HasHelp()) {
            // offer help on the button
        } else {
            HelpHandler::HandleHelp();
        }
    }
</PRE>

<A NAME="auto1064"></A>
<P><CODE>Dialog</CODE> implements a similar scheme, except that its
successor is not a widget but <EM>any</EM> help handler. In our
application this successor will be an instance of <CODE>Application</CODE>.</P>

<A NAME="auto1065"></A>
<PRE>
    class Dialog : public Widget {
    public:
        Dialog(HelpHandler* h, Topic t = NO_HELP_TOPIC);
        virtual void HandleHelp();
    
        // Widget 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();
        }
    }
</PRE>

<A NAME="app2"></A>
<P>At the end of the chain is an instance of <CODE>Application</CODE>.  The
application is not a widget, so <CODE>Application</CODE> is subclassed
directly from <CODE>HelpHandler</CODE>.
When a help request propagates to this level, the
application can supply information on the application in general, or
it can offer a list of different help topics:</P>

<A NAME="auto1067"></A>
<PRE>
    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
    }
</PRE>

<A NAME="dialog-231"></A>
<P>The following code creates and connects these objects.  Here the
dialog concerns printing, and so the objects have printing-related
topics assigned.</P>

<A NAME="auto1068"></A>
<PRE>
    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);
</PRE>

<A NAME="auto1069"></A>
<P>We can invoke the help request by calling <CODE>HandleHelp</CODE> on any
object on the chain.  To start the search at the button object, just
call <CODE>HandleHelp</CODE> on it:</P>

<A NAME="auto1070"></A>
<PRE>
    button->HandleHelp();
</PRE>

<A NAME="auto1071"></A>
<P>In this case, the button will handle the request immediately.  Note
that any <CODE>HelpHandler</CODE> class could be made the successor of
<CODE>Dialog</CODE>. Moreover, its successor could be changed
dynamically.  So no matter where a dialog is used, you'll get the
proper context-dependent help information for it.</P>

<A NAME="knownuses"></A>
<H2><A HREF="#relatedpatterns"><IMG SRC="down3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/down3.gif" BORDER=0 
ALT="next: Related Patterns"></A> Known Uses</H2> 

<A NAME="responder"></A>
<P>Several class libraries use the Chain of Responsibility pattern to
handle user events.  They use different names for the Handler class,
but the idea is the same: When the user clicks the mouse or presses a
key, an event gets generated and passed along the chain.
MacApp [<A HREF="bibfs.htm#macapp" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#macapp" TARGET="_mainDisplayFrame">App89</A>] and ET++ [<A HREF="bibfs.htm#et++" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#et++" TARGET="_mainDisplayF
rame">WGM88</A>] call it "EventHandler,"
Symantec's TCL library [<A HREF="bibfs.htm#think" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#think" TARGET="_mainDisplayFrame">Sym93b</A>] calls it "Bureaucrat," and
NeXT's AppKit [<A HREF="bibfs.htm#NeXT_AppKit" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#NeXT_AppKit" TARGET="_mainDisplayFrame">Add94</A>] uses the name "Responder."</P>

<A NAME="unidraw-use-cor"></A>
<A NAME="unidraw-use-comm"></A>
<P>The Unidraw framework for graphical editors defines Command objects
that encapsulate requests to Component and ComponentView
objects [<A HREF="bibfs.htm#unidraw_framework" tppabs="http://ultra/development/DesignPatterns/hires/bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>].  Commands are requests in the sense
that a component or component view may interpret a command to perform
an operation.  This corresponds to the "requests as objects"
approach described in Implementation.  Components and component views
may be structured hierarchically.  A component or a component view may
forward command interpretation to its parent, which may in turn
forward it to its parent, and so on, thereby forming a chain of
responsibility.</P>

<A NAME="auto1072"></A>
<P>ET++ uses Chain of Responsibility to handle graphical update.  A
graphical object calls the InvalidateRect operation whenever it must
update a part of its appearance. A graphical object can't handle
InvalidateRect by itself, because it doesn't know enough about its
context.  For example, a graphical object can be enclosed in objects
like Scrollers or Zoomers that transform its coordinate system.  That
means the object might be scrolled or zoomed so that it's partially
out of view.  Therefore the default implementation of InvalidateRect
forwards the request to the enclosing container object.  The last
object in the forwarding chain is a Window instance.  By the time
Window receives the request, the invalidation rectangle is guaranteed
to be transformed properly.  The Window handles InvalidateRect by
notifying the window system interface and requesting an update.</P>

<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="down3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/down3.gif" BORDER=0 ALT="next: 
navigation"></A> Related Patterns</H2> 

<A NAME="auto1073"></A>
<P>Chain of Responsibility is often applied in conjunction with <A
HREF="pat4cfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>. There,
a component's parent can act as its successor.</P>

<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="up3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat5bfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat5bfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/rightar3.gif"
        ALIGN=TOP BORDER=0></A> <A HREF="pat5bfs.htm" tppabs="http://ultra/development/DesignPatterns/hires/pat5bfs.htm"
        TARGET="_mainDisplayFrame">Command</A><BR>
<A HREF="chap5fs.htm" tppabs="http://ultra/development/DesignPatterns/hires/chap5fs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3.gif" tppabs="http://ultra/development/DesignPatterns/hires/gifsb/leftarr3.gif"
        ALIGN=TOP BORDER=0></A> <A HREF="chap5fs.htm" tppabs="http://ultra/development/DesignPatterns/hires/chap5fs.htm"
        TARGET="_mainDisplayFrame">Behavioral Patterns</A>
</P>


</BODY>

</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -