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

📄 pat5g-1.htm

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

<A NAME="auto1058"></A>
<PRE>
    void MySubject::Operation (int newValue) {
        BaseClassSubject::Operation(newValue);
            // trigger notification
    
        _myInstVar += newValue;
            // update subclass state (too late!)
    }
</PRE>

<A NAME="auto1059"></A>
<P>You can avoid this pitfall by sending notifications from template
methods (<A HREF="pat5jfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5jfs.htm" TARGET="_mainDisplayFrame">Template Method (325)</A>) in abstract Subject
classes. Define a primitive operation for subclasses to override, and make
Notify the last operation in the template method, which will ensure that
the object is self-consistent when subclasses override Subject
operations.</P>

<A NAME="auto1060"></A>
<PRE>
    void Text::Cut (TextRange r) {
         ReplaceRange(r);       // redefined in subclasses
         Notify();
    }
</PRE>

<A NAME="auto1061"></A>
<P>By the way, it's always a good idea to document which Subject operations
trigger notifications.</P>

</LI>

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

<A NAME="auto1063"></A>
<LI><EM>Avoiding observer-specific update protocols: the push
and pull models.</EM> Implementations of the Observer pattern often have
the subject broadcast additional information about the change.  The
subject passes this information as an argument to Update.  The amount
of information may vary widely.

<A NAME="pullmodel"></A>
<A NAME="pushmodel"></A>
<P>At one extreme, which we call the <STRONG>push model</STRONG>, the subject
sends observers detailed information about the change, whether they
want it or not.  At the other extreme is the <STRONG>pull model</STRONG>;
the subject sends nothing but the most minimal notification, and
observers ask for details explicitly thereafter.</P>

<A NAME="auto1064"></A>
<P>The pull model emphasizes the subject's ignorance of its observers,
whereas the push model assumes subjects know something about their
observers' needs.  The push model might make observers less reusable,
because Subject classes make assumptions about Observer classes that
might not always be true.  On the other hand, the pull model may be
inefficient, because Observer classes must ascertain what changed
without help from the Subject.</P>

</LI>

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

<A NAME="aspect"></A>
<LI><EM>Specifying modifications of interest explicitly.</EM>
You can improve update efficiency by extending the subject's
registration interface to allow registering observers only for
specific events of interest. When such an event occurs, the subject
informs only those observers that have registered interest in that
event. One way to support this uses the notion of
<STRONG>aspects</STRONG> for Subject objects. To register
interest in particular events, observers are attached to their
subjects using

<A NAME="auto1066"></A>
<PRE>
    void Subject::Attach(Observer*, Aspect&amp; interest);
</PRE>

<A NAME="auto1067"></A>
<P>where <CODE>interest</CODE> specifies the event
of interest.  At notification time, the subject supplies the changed
aspect to its observers as a parameter to the Update operation.  For
example:</P>

<A NAME="auto1068"></A>
<PRE>
    void Observer::Update(Subject*, Aspect&amp; interest);
</PRE>

</LI>

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

<A NAME="depend-manage"></A>
<A NAME="encap-complex"></A>
<A NAME="media-w-obsrv"></A>
<LI><EM>Encapsulating complex update semantics.</EM>
When the dependency relationship between subjects and observers is
particularly complex, an object that maintains these relationships might
be required.  We call such an object a <STRONG>ChangeManager</STRONG>.  Its
purpose is to minimize the work required to make observers reflect a
change in their subject.  For example, if an operation involves
changes to several interdependent subjects, you might have to
ensure that their observers are notified only after <EM>all</EM> the
subjects have been modified to avoid notifying observers more than
once.

<A NAME="auto1070"></A>
<P>ChangeManager has three responsibilities:</P>

<OL>

<A NAME="auto1071"></A>
<LI>It maps a subject to its observers and provides an interface to
maintain this mapping. This eliminates the need for subjects to maintain
references to their observers and vice versa.</LI>

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

<A NAME="auto1073"></A>
<LI>It defines a particular update strategy.</LI>

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

<A NAME="auto1075"></A>
<LI>It updates all dependent observers at the request of a subject.</LI>

</OL>

<A NAME="auto1076"></A>
<P>The following diagram depicts a simple ChangeManager-based implementation of
the Observer pattern. There are two specialized ChangeManagers.
SimpleChangeManager is naive in that it always updates all observers of
each subject. In contrast, DAGChangeManager handles directed-acyclic
graphs of dependencies between subjects and their observers. A
DAGChangeManager is preferable to a SimpleChangeManager when an observer
observes more than one subject. In that case, a change in two or more
subjects might cause redundant updates. The DAGChangeManager ensures
the observer receives just one update. SimpleChangeManager is
fine when multiple updates aren't an issue.</P>

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

<A NAME="auto1077"></A>
<P>ChangeManager is an instance of the <A HREF="pat5efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5efs.htm" TARGET="_mainDisplayFrame">Mediator (273)</A>
pattern.  In general there is only one ChangeManager, and it is known
globally.  The <A HREF="pat3efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3efs.htm" TARGET="_mainDisplayFrame">Singleton (127)</A> pattern would be
useful here.</P>

</LI>

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

<A NAME="subj-w-obser"></A>
<LI><EM>Combining the Subject and Observer classes.</EM>
Class libraries written in languages that lack multiple inheritance
(like Smalltalk) generally don't define separate Subject and Observer
classes but combine their interfaces in one class.  That lets you
define an object that acts as both a subject and an observer without
multiple inheritance.  In Smalltalk, for example, the Subject and
Observer interfaces are defined in the root class Object, making them
available to all classes.</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="auto1079"></A>
<P>An abstract class defines the Observer interface:</P>

<A NAME="auto1080"></A>
<PRE>
    class Subject;
    
    class Observer {
    public:
        virtual ~ Observer();
        virtual void Update(Subject* theChangedSubject) = 0;
    protected:
        Observer();
    };
</PRE>

<A NAME="auto1081"></A>
<P>This implementation supports multiple subjects for each observer.  The
subject passed to the <CODE>Update</CODE> operation lets the observer
determine which subject changed when it observes more than one.</P>

<A NAME="subject"></A>
<P>Similarly, an abstract class defines the Subject interface:</P>

<A NAME="auto1082"></A>
<PRE>
    class Subject {
    public:
        virtual ~Subject();
    
        virtual void Attach(Observer*);
        virtual void Detach(Observer*);
        virtual void Notify();
    protected:
        Subject();
    private:
        List&lt;Observer*> *_observers;
    };
    
    void Subject::Attach (Observer* o) {
        _observers->Append(o);
    }
    
    void Subject::Detach (Observer* o) {
        _observers->Remove(o);
    }
    
    void Subject::Notify () {
        ListIterator&lt;Observer*> i(_observers);
    
        for (i.First(); !i.IsDone(); i.Next()) {
            i.CurrentItem()->Update(this);
        }
    }
</PRE>

<A NAME="auto1083"></A>
<P><CODE>ClockTimer</CODE> is a concrete subject for storing and
maintaining the time of day. It notifies its observers every second.
<CODE>ClockTimer</CODE> provides the interface for retrieving individual
time units such as the hour, minute, and second.</P>

<A NAME="auto1084"></A>
<PRE>
    class ClockTimer : public Subject {
    public:
        ClockTimer();
    
        virtual int GetHour();
        virtual int GetMinute();
        virtual int GetSecond();
    
        void Tick();
    };
</PRE>

<A NAME="auto1085"></A>
<P>The <CODE>Tick</CODE> operation gets called by an internal timer at
regular intervals to provide an accurate time base.  <CODE>Tick</CODE>
updates the <CODE>ClockTimer</CODE>'s internal state and calls
<CODE>Notify</CODE> to inform observers of the change:</P>

<A NAME="auto1086"></A>
<PRE>
    void ClockTimer::Tick () {
        // update internal time-keeping state
        // ...
        Notify();
    }
</PRE>

<A NAME="digitalclock"></A>
<P>Now we can define a class <CODE>DigitalClock</CODE> that displays the
time. It inherits its graphical functionality from a <CODE>Widget</CODE>
class provided by a user interface toolkit. The Observer interface is
mixed into the <CODE>DigitalClock</CODE> interface by inheriting from
<CODE>Observer</CODE>.</P>

<A NAME="auto1087"></A>
<PRE>
    class DigitalClock: public Widget, public Observer {
    public:
        DigitalClock(ClockTimer*);
        virtual ~DigitalClock();
    
        virtual void Update(Subject*);
            // overrides Observer operation
    
        virtual void Draw();
            // overrides Widget operation;
            // defines how to draw the digital clock
    private:
        ClockTimer* _subject;
    };
    
    DigitalClock::DigitalClock (ClockTimer* s) {
        _subject = s;
        _subject->Attach(this);
    }
    
    DigitalClock::&nbsp;DigitalClock () {
        _subject->Detach(this);
    }
</PRE>

<A NAME="auto1088"></A>
<P>Before the <CODE>Update</CODE> operation draws the clock face, it checks
to make sure the notifying subject is the clock's subject:</P>

<A NAME="auto1089"></A>
<PRE>
    void DigitalClock::Update (Subject* theChangedSubject) {
        if (theChangedSubject == _subject) {
            Draw();
        }
    }
    
    void DigitalClock::Draw () {
        // get the new values from the subject
    
        int hour = _subject->GetHour();
        int minute = _subject->GetMinute();
        // etc.
    
        // draw the digital clock
    }
</PRE>

<A NAME="clock"></A>
<P>An <CODE>AnalogClock</CODE> class can be defined in the same way.</P>

<A NAME="auto1090"></A>
<PRE>
    class AnalogClock : public Widget, public Observer {
    public:
        AnalogClock(ClockTimer*);
        virtual void Update(Subject*);
        virtual void Draw();
        // ...
    };
</PRE>

<A NAME="auto1091"></A>
<P>The following code creates an <CODE>AnalogClock</CODE> and a
<CODE>DigitalClock</CODE> that always show the same time:</P>

<A NAME="auto1092"></A>
<PRE>
    ClockTimer* timer = new ClockTimer;
    AnalogClock* analogClock = new AnalogClock(timer);
    DigitalClock* digitalClock = new DigitalClock(timer);
</PRE>

<A NAME="auto1093"></A>
<P>Whenever the <CODE>timer</CODE> ticks, the two clocks will be updated
and will redisplay themselves appropriately.</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="smalltalk-use-observe"></A>
<A NAME="think-use-obsrv"></A>
<P>The first and perhaps best-known example of the Observer pattern appears
in Smalltalk Model/View/Controller (MVC), the user interface framework in the Smalltalk
environment [<A HREF="bibfs-1.htm#krasner_mvc" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#krasner_mvc" TARGET="_mainDisplayFrame">KP88</A>]. MVC's Model class plays the role of
Subject, while View is the base class for observers. Smalltalk,
ET++ [<A HREF="bibfs-1.htm#et++" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>], and the THINK class library [<A HREF="bibfs-1.htm#think" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#think" TARGET="_mainDisplayFrame">Sym93b</A>] provide a
general dependency mechanism by putting Subject and Observer interfaces
in the parent class for all other classes in the system.</P>

<A NAME="toolkit"></A>
<A NAME="unidraw-use-observ"></A>
<P>Other user interface toolkits that employ this pattern are
InterViews [<A HREF="bibfs-1.htm#interviews_composition" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#interviews_composition" TARGET="_mainDisplayFrame">LVC89</A>], the Andrew
Toolkit [<A HREF="bibfs-1.htm#atk" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#atk" TARGET="_mainDisplayFrame">P+88</A>], and Unidraw [<A HREF="bibfs-1.htm#unidraw_framework" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>]. InterViews
defines Observer and Observable (for subjects) classes explicitly.
Andrew calls them "view" and "data object," respectively. Unidraw
splits graphical editor objects into View (for observers) and Subject
parts.</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="auto1094"></A>
<P><A HREF="pat5efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5efs.htm" TARGET="_mainDisplayFrame">Mediator (273)</A>: By
encapsulating complex update semantics, the ChangeManager acts as
mediator between subjects and observers.</P>

<A NAME="auto1095"></A>
<P><A HREF="pat3efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3efs.htm" TARGET="_mainDisplayFrame">Singleton (127)</A>:
The ChangeManager may use the Singleton pattern to make it unique
and globally accessible.</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="pat5hfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5hfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/rightar3.gif"
        ALIGN=TOP BORDER=0></A> <A HREF="pat5hfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5hfs.htm" TARGET="_mainDisplayFrame">State</A><BR>
<A HREF="pat5ffs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5ffs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/leftarr3.gif"
        ALIGN=TOP BORDER=0></A> <A HREF="pat5ffs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5ffs.htm" TARGET="_mainDisplayFrame">Memento</A>
</P>

</BODY>

</HTML>

⌨️ 快捷键说明

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