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

📄 pat5h-1.htm

📁 四人帮《设计模式》一书英文版本
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<P></P>

<A NAME="auto1049"></A>
<P>The key difference between table-driven state machines and the State
pattern can be summed up like this: The State pattern models
state-specific behavior, whereas the table-driven approach focuses on
defining state transitions.</P>

</LI>

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

<A NAME="auto1051"></A>
<LI><EM>Creating and destroying State objects.</EM>
A common implementation trade-off worth considering is whether
(1) to create State objects only when they are needed and destroy them
thereafter versus (2) creating them ahead of time and never
destroying them.

<A NAME="auto1052"></A>
<P>The first choice is preferable when the states that will be entered
aren't known at run-time, <EM>and</EM> contexts change state
infrequently.  This approach avoids creating objects that won't be
used, which is important if the State objects store a lot of
information.  The second approach is better when state changes occur
rapidly, in which case you want to avoid destroying states, because
they may be needed again shortly.  Instantiation costs are paid once
up-front, and there are no destruction costs at all.  This approach
might be inconvenient, though, because the Context must keep
references to all states that might be entered.</P>

</LI>

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

<A NAME="dynamicinherit"></A>
<A NAME="self"></A>
<LI><EM>Using dynamic inheritance.</EM>
Changing the behavior for a particular request could be accomplished
by changing the object's class at run-time, but this is not possible
in most object-oriented programming languages.  Exceptions include
Self [<A HREF="bibfs-1.htm#ungar_self" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#ungar_self" TARGET="_mainDisplayFrame">US87</A>] and other delegation-based languages that
provide such a mechanism and hence support the State pattern directly.
Objects in Self can delegate operations to other objects to achieve a
form of dynamic inheritance.  Changing the delegation target at
run-time effectively changes the inheritance structure.  This
mechanism lets objects change their behavior and amounts to changing
their class.</LI>

</OL>

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

<A NAME="auto1054"></A>
<P>The following example gives the C++ code for the TCP connection
example described in the Motivation section.  This example is a
simplified version of the TCP protocol; it doesn't describe the
complete protocol or all the states of TCP
connections.<A NAME="fn8"></A><SUP><A HREF="#footnote8">8</A></SUP></P>

<A NAME="auto1055"></A>
<P>First, we define the class <CODE>TCPConnection</CODE>, which provides an
interface for transmitting data and handles requests to change state.</P>

<A NAME="auto1056"></A>
<PRE>
    class TCPOctetStream;
    class TCPState;
    
    class TCPConnection {
    public:
        TCPConnection();
    
        void ActiveOpen();
        void PassiveOpen();
        void Close();
    
        void Send();
        void Acknowledge();
        void Synchronize();
    
        void ProcessOctet(TCPOctetStream*);
    private:
        friend class TCPState;
        void ChangeState(TCPState*);
    private:
        TCPState* _state;
    };
</PRE>

<A NAME="tcpstate2"></A>
<P><CODE>TCPConnection</CODE> keeps an instance of the <CODE>TCPState</CODE>
class in the <CODE>_state</CODE> member variable.  The class
<CODE>TCPState</CODE> duplicates the state-changing interface of
<CODE>TCPConnection</CODE>. Each <CODE>TCPState</CODE> operation takes a
<CODE>TCPConnection</CODE> instance as a parameter, letting
<CODE>TCPState</CODE> access data from <CODE>TCPConnection</CODE> and
change the connection's state.</P>

<A NAME="auto1057"></A>
<PRE>
    class TCPState {
    public:
        virtual void Transmit(TCPConnection*, TCPOctetStream*);
        virtual void ActiveOpen(TCPConnection*);
        virtual void PassiveOpen(TCPConnection*);
        virtual void Close(TCPConnection*);
        virtual void Synchronize(TCPConnection*);
        virtual void Acknowledge(TCPConnection*);
        virtual void Send(TCPConnection*);
    protected:
        void ChangeState(TCPConnection*, TCPState*);
    };
</PRE>

<A NAME="auto1058"></A>
<P><CODE>TCPConnection</CODE> delegates all state-specific requests to its
<CODE>TCPState</CODE> instance <CODE>_state</CODE>.
<CODE>TCPConnection</CODE> also provides an operation for changing this
variable to a new <CODE>TCPState</CODE>.  The constructor for
<CODE>TCPConnection</CODE> initializes the object to the
<CODE>TCPClosed</CODE> state (defined later).</P>

<A NAME="auto1059"></A>
<PRE>
    TCPConnection::TCPConnection () {
        _state = TCPClosed::Instance();
    }
    
    void TCPConnection::ChangeState (TCPState* s) {
        _state = s;
    }
    
    void TCPConnection::ActiveOpen () {
        _state->ActiveOpen(this);
    }
    
    void TCPConnection::PassiveOpen () {
        _state->PassiveOpen(this);
    }
    
    void TCPConnection::Close () {
        _state->Close(this);
    }
    
    void TCPConnection::Acknowledge () {
        _state->Acknowledge(this);
    }
    
    void TCPConnection::Synchronize () {
        _state->Synchronize(this);
    }
</PRE>

<A NAME="auto1060"></A>
<P><CODE>TCPState</CODE> implements default behavior for all requests
delegated to it.  It can also change the state of a
<CODE>TCPConnection</CODE> with the <CODE>ChangeState</CODE> operation.
<CODE>TCPState</CODE> is declared a friend of <CODE>TCPConnection</CODE> to
give it privileged access to this operation.</P>

<A NAME="auto1061"></A>
<PRE>
    void TCPState::Transmit (TCPConnection*, TCPOctetStream*) { }
    void TCPState::ActiveOpen (TCPConnection*) { }
    void TCPState::PassiveOpen (TCPConnection*) { }
    void TCPState::Close (TCPConnection*) { }
    void TCPState::Synchronize (TCPConnection*) { }
    
    void TCPState::ChangeState (TCPConnection* t, TCPState* s) {
        t->ChangeState(s);
    }
</PRE>

<A NAME="auto1062"></A>
<P>Subclasses of <CODE>TCPState</CODE> implement state-specific behavior. A
TCP connection can be in many states: Established, Listening, Closed,
etc., and there's a subclass of <CODE>TCPState</CODE> for each state.
We'll discuss three subclasses in detail: <CODE>TCPEstablished</CODE>,
<CODE>TCPListen</CODE>, and <CODE>TCPClosed</CODE>.</P>

<A NAME="auto1063"></A>
<PRE>
    class TCPEstablished : public TCPState {
    public:
        static TCPState* Instance();
    
        virtual void Transmit(TCPConnection*, TCPOctetStream*);
        virtual void Close(TCPConnection*);
    };
    
    class TCPListen : public TCPState {
    public:
        static TCPState* Instance();
    
        virtual void Send(TCPConnection*);
        // ...
    };
    
    class TCPClosed : public TCPState {
    public:
        static TCPState* Instance();
    
        virtual void ActiveOpen(TCPConnection*);
        virtual void PassiveOpen(TCPConnection*);
        // ...
    };
</PRE>

<A NAME="auto1065"></A>
<P><CODE>TCPState</CODE> subclasses maintain no local state, so
they can be shared, and only one instance of each is required.  The
unique instance of each <CODE>TCPState</CODE> subclass is obtained by the
static <CODE>Instance</CODE>
operation.<A NAME="fn9"></A><SUP><A HREF="#footnote9">9</A></SUP></P>

<A NAME="auto1066"></A>
<P>Each <CODE>TCPState</CODE> subclass implements state-specific behavior
for valid requests in the state:</P>

<A NAME="auto1067"></A>
<PRE>
    void TCPClosed::ActiveOpen (TCPConnection* t) {
        // send SYN, receive SYN, ACK, etc.
    
        ChangeState(t, TCPEstablished::Instance());
    }
    
    void TCPClosed::PassiveOpen (TCPConnection* t) {
        ChangeState(t, TCPListen::Instance());
    }
    
    void TCPEstablished::Close (TCPConnection* t) {
        // send FIN, receive ACK of FIN
    
        ChangeState(t, TCPListen::Instance());
    }
    
    void TCPEstablished::Transmit (
        TCPConnection* t, TCPOctetStream* o
    ) {
        t->ProcessOctet(o);
    }
    
    void TCPListen::Send (TCPConnection* t) {
        // send SYN, receive SYN, ACK, etc.
    
        ChangeState(t, TCPEstablished::Instance());
    }
</PRE>

<A NAME="auto1068"></A>
<P>After performing state-specific work, these operations call the
<CODE>ChangeState</CODE> operation to change the state of
the <CODE>TCPConnection</CODE>.  <CODE>TCPConnection</CODE> itself doesn't
know a thing about the TCP connection protocol; it's the
<CODE>TCPState</CODE> subclasses that define each state transition
and action in TCP.</P>

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

<A NAME="auto1069"></A>
<P>Johnson and Zweig [<A HREF="bibfs-1.htm#johnson-zweig_delegation" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#johnson-zweig_delegation" TARGET="_mainDisplayFrame">JZ91</A>] characterize the
State pattern and its application to TCP connection protocols.</P>

<A NAME="auto1070"></A>
<P>Most popular interactive drawing programs provide "tools" for
performing operations by direct manipulation.  For example, a
line-drawing tool lets a user click and drag to create a new line.  A
selection tool lets the user select shapes.  There's usually a palette
of such tools to choose from.  The user thinks of this activity as
picking up a tool and wielding it, but in reality the editor's
behavior changes with the current tool: When a drawing tool is active
we create shapes; when the selection tool is active we select shapes;
and so forth.  We can use the State pattern to change the editor's
behavior depending on the current tool.</P>

<A NAME="tool-class"></A>
<P>We can define an abstract Tool class from which to define subclasses
that implement tool-specific behavior.  The drawing editor maintains a
current Tool object and delegates requests to it.  It replaces this
object when the user chooses a new tool, causing the behavior of the
drawing editor to change accordingly.  </P>

<A NAME="unidraw-use-state"></A>
<P>This technique is used in both the HotDraw [<A HREF="bibfs-1.htm#hotdraw" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#hotdraw" TARGET="_mainDisplayFrame">Joh92</A>] and 
Unidraw [<A HREF="bibfs-1.htm#unidraw_framework" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>] drawing editor frameworks. It allows 
clients to define new kinds of tools easily. In HotDraw, the 
DrawingController class forwards the requests to the current Tool object. 
In Unidraw, the corresponding classes are Viewer and Tool. The following 
class diagram sketches the Tool and DrawingController interfaces:</P>

<A NAME="tool-313c"></A>
<P ALIGN=CENTER><IMG SRC="state012-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/state012.gif"></P>

<A NAME="envelope-letter"></A>
<P>Coplien's Envelope-Letter idiom [<A HREF="bibfs-1.htm#coplien_idioms" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#coplien_idioms" TARGET="_mainDisplayFrame">Cop92</A>] is related to
State.  Envelope-Letter is a technique for changing an object's class at
run-time. The State pattern is more specific, focusing on how to deal
with an object whose behavior depends on its state.</P>

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

<A NAME="auto1072"></A>
<P>The <A HREF="pat4ffs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4ffs.htm" TARGET="_mainDisplayFrame">Flyweight (195)</A>
pattern explains when and how State objects can be shared.</P>

<A NAME="auto1073"></A>
<P>State objects are often
<A HREF="pat3efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3efs.htm" TARGET="_mainDisplayFrame">Singletons (127)</A>.</P>

<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="up3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat5ifs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5ifs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/rightar3.gif"
        ALIGN=TOP BORDER=0></A> <A HREF="pat5ifs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5ifs.htm"
        TARGET="_mainDisplayFrame">Strategy</A><BR>
<A HREF="pat5gfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5gfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/leftarr3.gif"
        ALIGN=TOP BORDER=0></A> <A HREF="pat5gfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5gfs.htm"
        TARGET="_mainDisplayFrame">Observer</A>
</P>

<HR>

<A NAME="footnote8"></A>
<SUP>8</SUP>This example is based on the TCP connection protocol
described by Lynch and
Rose [<A HREF="bibfs-1.htm#lynch-rose_internet" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#lynch-rose_internet" TARGET="_mainDisplayFrame">LR93</A>].
</P>

<A NAME="footnote9"></A>
<SUP>9</SUP>This makes each <CODE>TCPState</CODE> subclass a Singleton (see
<A HREF="pat3efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3efs.htm" TARGET="_mainDisplayFrame">Singleton (127)</A>).
</P>
 
</BODY>

</HTML>

⌨️ 快捷键说明

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