observer.h

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  * \file observer.h <mi32/observer.h>
00004  * \brief Observer Pattern Class definition
00005  *
00006  * Currently, this only implements the Client/Server type of Observer Pattern.
00007  * That is, one in which you have a SUBJECT class which hangs around and is
00008  * a source of events, and multiple clients or OBSERVERs which come and go and
00009  * want to recieve messages from the SUBJECT.
00010  *
00011  * The other form of Observer Pattern is the Big Brother approach.  That is,
00012  * You have one OBSERVER which monitors the coming and going of SUBJECTS.
00013  * This is a bit more complicated because in the generic case, your SUBJECTs
00014  * could be in multiple threads.  This isn't implemented yet.
00015  *
00016  * \if NODOC
00017  * $Id: observer.h_v 1.19 2003/09/15 15:37:51 dwilliss Exp $
00018  *
00019  * $Log: observer.h_v $
00020  * Revision 1.19  2003/09/15 15:37:51  dwilliss
00021  * More mismatched #ifdefs
00022  *
00023  * Revision 1.18  2003/09/15 15:28:13  dwilliss
00024  * Fixed doxygen cleanup errors. Unbalanced ifdefs
00025  *
00026  * Revision 1.17  2003/09/15 13:49:56  fileserver!dwilliss
00027  * Doxygen
00028  *
00029  * Revision 1.16  2003/09/11 20:15:24  dwilliss
00030  * Added typename keyword to avoid g++ compiler warnings
00031  *
00032  * Revision 1.15  2003/04/09 19:20:28  dwilliss
00033  * Don't use _T in templates.  Mac's ctype.h defines a global _T.  Thanks Apple!
00034  *
00035  * Revision 1.14  2003/01/24 14:22:55  mju
00036  * Attempt to fix Unix warnings with typedef of OBSERVERLIST_ITERATOR.
00037  *
00038  * Revision 1.13  2002/09/13 20:50:24  dwilliss
00039  * Nope!  MS VC++ acccepted that, but gcc changed the warning to an error!
00040  *
00041  * Revision 1.12  2002/09/13 20:28:12  dwilliss
00042  * Try to fix a "typename is implicitly defined" warning from gcc 3.1
00043  *
00044  * Revision 1.11  2002/08/26 14:46:44  dwilliss
00045  * Clear up some warnings from new gcc 3.1 (I hope)
00046  *
00047  * Revision 1.10  2000/10/16 15:05:13  dwilliss
00048  * Internal iterator constructor should take const reference, not by value
00049  *
00050  * Revision 1.9  2000/10/13 16:20:07  dwilliss
00051  * Be more specific about who our friends are
00052  *
00053  * Revision 1.8  2000/10/13 16:04:16  dwilliss
00054  * Mac doesn't like typedef of MILIST<_CT*> OBSERVERLIST
00055  *
00056  * Revision 1.7  2000/10/13 14:59:40  dwilliss
00057  * Must use "friend class NAME", not "friend NAME"
00058  *
00059  * Revision 1.6  2000/10/12 16:15:52  mju
00060  * Keep sample code out of Genitor docs.
00061  *
00062  * Revision 1.5  2000/08/24 14:13:54  dwilliss
00063  * Changed ITERATOR's operator-> to hopefully compile on Mac
00064  *
00065  * Revision 1.4  2000/08/23 14:13:16  dwilliss
00066  * Fixed a Unix compile problem
00067  *
00068  * Revision 1.3  2000/08/23 13:53:44  dwilliss
00069  * Trying to fix weird Mac-only compile error involving const.
00070  *
00071  * Revision 1.2  2000/08/21 21:26:52  dwilliss
00072  * *** empty log message ***
00073  *
00074  * Revision 1.1  2000/08/01 17:30:58  dwilliss
00075  * Initial revision
00076  *
00077  * \endif
00078  *****************************************************************************/
00079 
00080 #ifndef INC_MI32_OBSERVER_H
00081 #define INC_MI32_OBSERVER_H
00082 
00083 #ifndef INC_MI32_MILIST_H
00084 #include <mi32/milist.h>
00085 #endif
00086 
00087 #ifndef GENERATING_DOXYGEN_OUTPUT
00088 class OBSERVER_BASE;
00089 
00090 //! Don't derive from this class directly.  It's the base class for
00091 //! SUBJECT<>
00092 class SUBJECTBASE {
00093    public:
00094 
00095       SUBJECTBASE (
00096          ) {
00097          }
00098 
00099       virtual ~SUBJECTBASE (
00100          ) {
00101          }
00102 
00103    private:
00104       //! The following virtual methods are private.  Only the friend
00105       //! class OBSERVER_BASE may call them
00106       virtual void AddObserver (
00107          OBSERVER_BASE* observer
00108          ) = 0;
00109 
00110       virtual void RemoveObserver (
00111          OBSERVER_BASE* observer
00112          ) = 0;
00113 
00114       friend class OBSERVER_BASE;
00115    };
00116 #endif //!< GENERATING_DOXYGEN_OUTPUT
00117 
00118 
00119 //! Base class for the SUBJECT of the OBSERVER (the observed).
00120 //! For an example of how to use this, see the end of this include file.
00121 template <class _CT = class OBSERVER_BASE> class SUBJECT : public SUBJECTBASE {
00122    public:
00123 
00124       typedef MILIST<_CT*> OBSERVERLIST;
00125       typedef typename OBSERVERLIST::ITERATOR OBSERVERLIST_ITERATOR;
00126 
00127       class ITERATOR {
00128          public:
00129             //! Default constructor
00130             ITERATOR (
00131                ) {}
00132 
00133             //No copy constructor?  Hmmm.. MILIST<>ITERATOR didn't have one
00134             // and that's what I used as a reference for this.
00135 
00136             //! Dereference operator - returns ref to item
00137             _CT*& operator* ( 
00138                ) const {
00139                return (*m_it);
00140                }
00141 
00142             //! Dereference operator ptr to item
00143             _CT* operator-> ( 
00144                ) {
00145                return (*m_it);
00146                }
00147 
00148             //! Pre-increment operator
00149             ITERATOR& operator++ (
00150                ) {
00151                m_it = m_next;
00152                ++m_next;
00153                return (*this);
00154                }
00155 
00156             bool operator== (
00157                const ITERATOR& rhs
00158                ) const {
00159                return (m_it == rhs.m_it);
00160                }
00161 
00162             bool operator!= (
00163                const ITERATOR& rhs
00164                ) const {
00165                return (m_it != rhs.m_it);
00166                }
00167 
00168          private:
00169             #ifndef GENERATING_DOXYGEN_OUTPUT
00170             friend class SUBJECT<_CT>;
00171             OBSERVERLIST_ITERATOR m_it;
00172             OBSERVERLIST_ITERATOR m_next;
00173 
00174             //! Internal constructor
00175             ITERATOR (
00176                const OBSERVERLIST_ITERATOR& it
00177                ) {
00178                m_it = it;
00179                m_next = it;
00180                ++m_next;
00181                }
00182 
00183             //! Used internally by SUBJECT<>::RemoveItem()
00184             OBSERVERLIST_ITERATOR& GetIterator (
00185                ) {
00186                return (m_it);
00187                }
00188             #endif //!< GENERATING_DOXYGEN_OUTPUT
00189 
00190          };
00191 
00192       //! Default constructor. (nothing to do).
00193       SUBJECT (
00194          ) {
00195          }
00196 
00197       //! Destructor.
00198       //! Iterates through the list of OBSERVERs and calls their OnSubjectDestroy methods.
00199       virtual ~SUBJECT (
00200          ) {
00201          for (ITERATOR it = ObserverBegin() ; it != ObserverEnd() ; ++it) {
00202 
00203             (*it)->OnSubjectDestroy();
00204             }
00205          }
00206 
00207       //! Call the OnNotify method of all the observers.
00208       //! If other event handlers are needed, they would be implemented as
00209       //! virtual methods in the OBSERVER-derived class, and the SUBJECT-
00210       //! derived class would be given methods to call them.
00211       void NotifyObservers (
00212          ) {
00213          for (ITERATOR it = ObserverBegin() ; it != ObserverEnd() ; ++it) {
00214 
00215             (*it)->OnNotify();
00216             }
00217          return;
00218          }
00219 
00220    protected:
00221 
00222       //! Return an iterator to the first Observer.
00223       //! Note: Named ObserverBegin() to avoid possible conflict if derived
00224       //! class wants a Begin() method.
00225       ITERATOR ObserverBegin (
00226          ) {
00227          return (ITERATOR(m_Observers.Begin()));
00228          }
00229 
00230       //! Return an iterator to the end of the Observer list
00231       //! Note: Named ObserverBegin() to avoid possible conflict if derived
00232       //! class wants a Begin() method.
00233       ITERATOR ObserverEnd (
00234          ) {
00235          return (ITERATOR(m_Observers.End()));
00236          }
00237 
00238    private:
00239       #ifndef GENERATING_DOXYGEN_OUTPUT
00240       MILIST<_CT*> m_Observers;
00241 
00242       //! Add an observer.  
00243       void AddObserver (
00244          OBSERVER_BASE* observer
00245          ) {
00246          m_Observers.PushFront(static_cast<_CT*>(observer));
00247          }
00248 
00249       //! Remove an observer.  
00250       //! Called by OBSERVER's destructor. Only OBSERVER should be able to to 
00251       //! call this, so it's private and OBSERVER is a friend class.
00252       void RemoveObserver (
00253          OBSERVER_BASE* observer
00254          ) {
00255          for (ITERATOR it = ObserverBegin() ; it != ObserverEnd() ; ++it) {
00256             if ((*it) == static_cast<_CT*>(observer)) {
00257                m_Observers.Remove(it.GetIterator());
00258                break;
00259                }
00260             }  
00261          return;
00262          }
00263       #endif //!< GENERATING_DOXYGEN_OUTPUT
00264 
00265    };
00266 
00267 
00268 //! Base class for an Observer.
00269 //!
00270 //! For simple cases, there is a virtual method Notify() which the
00271 //! SUBJECT can call.  For more complicated usage, see the example
00272 //! at the end of this include file.
00273 //!
00274 class OBSERVER_BASE {
00275    public:
00276 
00277       //! Constructor taking a SUBJECT reference.
00278       //! Will arrange to be notified when subject 
00279       //! changes anything
00280       OBSERVER_BASE (
00281          SUBJECTBASE& subject
00282          ) :
00283          m_subject(&subject),
00284          m_bAttached(false)      //! Attach will set this to true
00285          {
00286          Attach();
00287          }
00288          
00289       //! Copy constructor
00290       OBSERVER_BASE (
00291          const OBSERVER_BASE& rhs
00292          ) :
00293          m_subject(rhs.m_subject),
00294          m_bAttached(false)      //! Attach will set this to true
00295          {
00296          Attach();
00297          }
00298          
00299       //! Destructor
00300       virtual ~OBSERVER_BASE (
00301          ) {
00302          //! Remove ourselves from the SUBJECT's observer list.  
00303          //! SUBJECT's destructor will call our OnSubjectDestroy method, which will set 
00304          //! m_subject to NULL so that we won't try to remove ourselves from 
00305          //! a subject that no longer exists.
00306          Detach();
00307          }
00308 
00309             //! Assignment operator.
00310       OBSERVER_BASE& operator= (
00311          const OBSERVER_BASE& rhs
00312          ) {
00313          //! Don't bother doing anything unless rhs points to a different SUBJECT
00314          if (&rhs != this && rhs.m_subject != m_subject) {
00315             if (m_subject != 0) m_subject->RemoveObserver(this);
00316             m_subject = rhs.m_subject;
00317             if (m_subject != 0) m_subject->AddObserver(this);
00318             }
00319          return (*this);
00320          }
00321 
00322 
00323       void Attach (
00324          ) {
00325          if (m_subject == 0 || m_bAttached) return;
00326          m_subject->AddObserver(this);
00327          m_bAttached = true;
00328          return;
00329          }
00330 
00331       void Detach (
00332          ) {
00333          if (m_subject == 0 || !m_bAttached) return;
00334          m_subject->RemoveObserver(this);
00335          m_bAttached = false;
00336          return;
00337          }
00338 
00339       bool IsAttached (
00340          ) const {
00341          return (m_bAttached);
00342          }
00343 
00344       //! Called by m_subject's destructor to tell us it's going away.
00345       //! (Could be made private if we made SUBJECT a friend)
00346       void OnSubjectDestroy (
00347          ) {
00348          m_subject = 0;
00349          }
00350 
00351       //! Virtual method which the SUBJECT will call when it's NotifyObservers()
00352       //! method is called.
00353       //! If other event handlers are needed, they would be implemented as
00354       //! virtual methods in the OBSERVER-derived class, and the SUBJECT-
00355       //! derived class would be given methods to call them.
00356       virtual void OnNotify (
00357          ) {
00358          }
00359 
00360    private:
00361       #ifndef GENERATING_DOXYGEN_OUTPUT
00362       SUBJECTBASE* m_subject;
00363       bool m_bAttached;
00364       #endif //!< GENERATING_DOXYGEN_OUTPUT
00365 
00366    };
00367 
00368 
00369 
00370 #ifndef GENERATING_DOXYGEN_OUTPUT
00371 #if 0
00372 //! Example Code
00373 
00374 #ifndef INC_MI32_RECT_H
00375 #include <mi32/rect.h>
00376 #endif
00377 
00378 class THINGY;
00379 
00380 //! Example of a derived SUBJECT which knows about the event handling
00381 //! methods of a class derived from OBSERVER_BASE.
00382 //! When declaring the derived class, you have to also specify which
00383 //! OBSERVER_BASE-derived class you want this to be the subject of.
00384 class EVENTPUMP : public SUBJECT<THINGY> {
00385    public:
00386 
00387       //! Method to call THINGY::OnRedraw().
00388       //! Actual method can't be defined inline.  We can't reference things
00389       //! in THINGY until we've defined EVENTPUMP.  And we can't define
00390       //! THINGY until we've defined EVENTPUMP.  I tried forward declaring
00391       //! class EVENTPUMP : public SUBJECT<THINGY> but that won't compile.
00392       void CallOnRedraw (
00393          const DRECT2D& rect
00394          );
00395       };
00396 
00397 //! Example of an OBSERVER-derived class.  
00398 //! This class adds the additional event handler OnDraw().
00399 class THINGY : public OBSERVER_BASE {
00400    public:
00401       THINGY (
00402          EVENTPUMP& pump
00403          ) :
00404          OBSERVER_BASE(pump)
00405          {
00406          }
00407 
00408       virtual ~THINGY (
00409          ) {
00410          }
00411          
00412       //! Example of adding a new event handler to the derived class       
00413       //! New handler can take anything you want, but the SUBJECT-derived
00414       //! class has to know to call it.
00415       virtual void OnRedraw (
00416          const DRECT2D& rect
00417          );
00418    };
00419 
00420 
00421 void EVENTPUMP::CallOnRedraw (
00422    const DRECT2D& rect
00423    ) {
00424    for (ITERATOR it = ObserverBegin() ; it != ObserverEnd() ; ++it) {
00425 
00426       (*it)->OnRedraw(rect);
00427       }
00428    return;
00429    }
00430 
00431 #endif      //!< End of example code
00432 #endif //!< GENERATING_DOXYGEN_OUTPUT
00433 
00434 #endif      //!< INC_MI32_OBSERVER_H
00435 

Generated on Tue Dec 14 13:18:31 2004 for TNTsdk by  doxygen 1.3.8-20040913