(beginning of part II)
Subjects
Subscription process
What is stored?
Depending on the particular implementation, entities may store the following data when subscribers subscribe:
- Event id Interest or what event is signed by the observer.
- An instance of an observer is most often in the form of an object pointer.
- Member function pointer - if an arbitrary handler is used.
This data will form the parameters of the subscription method:
// Subscription with an overridden handler (where the observer class has a base class handler method). aSubject->Subscribe( "SizeChanged", this ); // Subscription with an arbitrary handler. aSubject->Subscribe( "SizeChanged", this, &ThisObserverClass::OnSizeChanged );
It is worth noting that if an arbitrary handler is used, pointers to member functions are likely to be packaged with an observer instance in a class or struct to form a delegate. And so the Subscribe() method may have the following signature:
// Delegate = object pointer + member function pointer. void Subject::Subscribe( EventId aEventId, Delegate aDelegate ) { //... }
The actual save (possibly within std::map ) will include the event identifier as a key and delegate as a value.
Implement event identifiers
Defining event identifiers outside the class of objects that trigger them can simplify access to these identifiers. But, generally speaking, the events released by an item are unique to that item. Thus, in most cases, it is logical to declare event identifiers in the topic class.
Although there are several ways to declare event identifiers, only 3 of the most interest are discussed here:
Enums seem at first glance the most logical choice:
class FigureSubject : public Subject { public: enum { evSizeChanged, evPositionChanged }; };
Comparison of transfers (which will occur during subscription and shelling) is quick. Perhaps the only inconvenience in this strategy is that observers should indicate the class after subscribing:
// 'FigureSubject::' is the annoying bit. aSubject->Subscribe( FigureSubject::evSizeChanged, this );
Strings provide the "looser" option for an enumeration, since usually an object class does not declare them as an enumeration; instead, customers will use:
// Observer code aFigure->Subscribe( "evSizeChanged", this );
The good thing about strings is that most compilers color them differently from other parameters, which somehow improves the readability of the code:
But the problem with the strings is that we cannot say at run time if we mistakenly wrote the name of the event. In addition, string comparison takes longer than enumeration comparison, since strings need to be compared by characters.
Types is the last option discussed here:
class FigureSubject : public Subject { public:
The advantage of using types is that they allow you to overload methods such as Subscribe() (which we will see soon, can solve a common problem with observers):
But then again, observers need some extra code to subscribe:
// Observer code aFigure->Subscribe( aFigure->SizeChangedEvent, this );
Where to store observers?
Clause 1 of the implementation in the design template relates to where observers for each object should be stored. This section adds to this discussion by providing 3 options:
- Global hash
- Per object
- Per event
As shown in the design patterns, one place to store the map of the observer subject is in the global hash table. , ( ). , - - . , javascript - , . , - , .
Design Patterns , . ( - std::map ), , , , , :
class Subject { protected:
Design Patterns , -, . , -, std::vector . , , . . , :
class Event { public: void Subscribe( void *aDelegate ); void Unsubscribe( void *aDelegate ); void Fire(); };
:
class ConcreteSubject : public Subject { public:
, , , :
3 store-vs-compute. :

:
MouseMove , . , . Given:
8 1 ( ).
, ?
, ( ).
, , std::multimap std::map . , :
aSubject->Unsubscribe( evSizeChanged, this );
, ( !), . , Subscribe() , Unsubscribe() , .
, - ? :
class Figure { public: Figure( Subject *aSubject ) { // We subscribe to the subject on size events aSubject->Subscribe( evSizeChanged, this, &Figure::OnSizeChanged ); } void OnSizeChanged( Size aSize ) { } }; class Circle : public Figure { public: Circle( Subject *aSubject ) : Figure( aSubject) { // We subscribe to the subject on size events aSubject->Subscribe( evSizeChanged, this, &Circle::OnSizeChanged ); } void OnSizeChanged( Size aSize ) { } };
, . , OnSizeChanged() , - . , -, :
aSubject->Unsubscribe( evSizeChanged, this, &Circle::OnSizeChanged );
OnSizeChanged() , .
, OnSizeChanged() , , Circle , , , :
class Figure { public:
, , , , , . , , , .
. - ( ), Unsubscribe() , ( MFP Subscribe() ):
aSubject->Unsubscribe( evSizeChanged, this );
, - , .
, , , , . Consider this code:
class Figure { public:
Figure , , , , .
, . - :
class Figure { public: Figure( FigureSubject *aSubject ) {
12 . , , .
Subscribe() :
:
class Figure { public: Figure( FigureSubject *aSubject ) {
, Fire ( aDelegate ), , , .
gxObserver , . , ( ) -:
class Subject : virtual public gxSubject { public: gxDefineBoundEvent( evAge, int, GetAge() ) int GetAge() { return mAge; } private: int mAge; }
, :
// Same as Fire( evAge, GetAge() ); Fire( evAge );
, :
JUCE :
void Button::sendClickMessage (const ModifierKeys& modifiers) { for (int i = buttonListeners.size(); --i >= 0;) { ButtonListener* const bl = (ButtonListener*) buttonListeners[i]; bl->buttonClicked (this); } }
:
- ,
buttonListeners , , AddListener RemoveListener . - - , .
- , (
ButtonListener ), ( buttonClicked ).
, . , / . - .
, , ; , (, ) . , :
. wed, , , , . :
class Subject { public: void SuspendEvents( bool aQueueSuspended ); void ResumeEvents(); };
, , . , , , . , , (, evBeforeDestroy ):

, evBeforeDestroy - , ( ), , , ( ).
, . , , , . wed , , . , .
, . (10,10), (20,20). , - , .
?
:
class Subject { public: virtual void Subscribe( aEventId, aDelegate ); virtual void Unsubscribe( aEventId, aDelegate ); virtual void Fire( aEventId ); }
, . There are three options:
ConcreteSubject Subject .
class ScrollManager: public Subject { }
, , . . : , ; Composite Subject ? , , , , .
Composition
" , . . mSubject , , :
class ScrollManager: public SomeObject { public: Subject mSubject; }
, (-) , . , :
, :
class ScrollManager: public SomeObject, public virtual Subject { }
, mSubject , :
, public virtual , , ScrollManager , . , , , .
, , , , . ExtJs, Javascript, , mixins :
Ext.define('Employee', { mixins: { observable: 'Ext.util.Observable' }, constructor: function (config) { this.mixins.observable.constructor.call(this, config); } });
Conclusion
:
- , - push.
- .
- . subject-observer .
- push-; .
- .
- , . , .
- .
- - -, .
- , .
- .
( II)