
Introduction
The Speaker-Listener Design Pattern allows to add new operations to existing objects without modifying the structure of the objects. In essence, the pattern allows to the interested parties (the listeners) to be notified when an event has occurred and gives them a chance to take actions.
Background
This pattern follows the open/closed principle which states that "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification" -- The behaviour of the speaker object (and the algorithm which it implements) can be modified without altering its source code. This saves the effort and the precious time on testing, code reviews, and debugging.
Using the code
The Speaker object sends events to parties interested in receiving Listener events.
First, we have to decide what events the speaker should fire. A Listener is a class that has one or more functions with the following signature:
public : void on( Event event , ... ) throw();
class SimpleListener {
public:
template<int>
class Event {
};
enum id {
EVENT_ADD, EVENT_SUB };
virtual void on( Event<event_add> event , int x , int y) = 0;
virtual void on( Event<event_sub> event , int x , int y) = 0;
};
In order to add to speaker object the capability to send events, we inherit from Speaker<SimpleListener>. By calling fire() when an event has occurred, we notify the registered listeners about the event and allow them to take actions.
class SimpleSpeaker: public Speaker<SimpleListener> {
public: void add(int x, int y) {
printf( "Speaker : add %d and %d\n",x,y);
fire( SimpleListener::Event<SimpleListener::EVENT_ADD>() , x , y );
}
public: void sub(int x, int y) {
printf( "Speaker : sub %d and %d\n",x,y);
fire( SimpleListener::Event<SimpleListener::EVENT_SUB>() , x , y );
}
};
To create listener classes, we inherit from SimpleListener. Let's create two listener classes that print messages to the screen.
class Listener1 : public SimpleListener {
public:
virtual void on( Event<EVENT_ADD> event , int x , int y){
printf( "L1: add %d and %d\n",x,y);
}
virtual void on( Event<EVENT_SUB> event , int x , int y){
printf( "L1: sub %d and %d\n",x,y);
}
};
class Listener2 : public SimpleListener {
public:
virtual void on( Event<EVENT_ADD> event , int x , int y){
printf( "L2: add %d and %d\n",x,y);
}
virtual void on( Event<EVENT_SUB> event , int x , int y){
printf( "L2: sub %d and %d\n",x,y);
}
};
To be notified about the events, the listener object should be registered with addListener of the speaker object. To stop being notified, use removeListener() and removeListeners().
int main() {
SimpleSpeaker ss;
ss.addListener( new Listener1() , true ); ss.add(1,2);
ss.addListener( new Listener2() , true );
ss.sub(3,4);
ss.removeListeners();
ss.sub(3,4);
}
Summary
Although Boost.Signals is a good implementation to this pattern, I found that my implementation is more simple to use. The relationships between the objects are documented in the code, thereby creating more readable and maintainable code.