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
1.3.8-20040913