What is Observer Design Pattern
Observer design patttern is a type of behavioral pattern which is mainly used when state change of one object, impacts other objects state or behavior. In other words, when one object changes then it induces other objects to take some action. In such cases, the observer pattern is the best fit.
This pattern works on publisher-subscriber principle as below:
Subscriber
Objects that need to monitor specific object. These objects will subscribe for event or state change notification.
Publisher
This is the monitored object. Once it changes it will publish or notify all subscribed objects.
Why
Let us try to understand the need of Observer pattern by taking an example of a mobile application whose job is to play songs. So, whenever, the user select some new song, the application needs to inform or update other components about new song selection.
Below are the objects that need to monitor the song object state:
- Screen object – showing new song details.
- Audio speaker object – playing new song.
- Icon object – showing new song icon.
Sample application without using Observer Design Pattern
One simple way of implementing above application is :
- Firstly, create all objects.
- Secondly – on song object state change inform all other objects.
Below is the sample application code without Observer pattern –
#include<iostream> //main header
using namespace std;//for namespace
class songs
{
public:
void selectnewsong()
{
cout<<"new song selected"<<endl;
}
};
class screen
{
public:
void updateScreen()
{
cout<<"new song data"<<endl;
}
};
class icon
{
public:
void updateIcon()
{
cout<<"new Icon loaded"<<endl;
}
};
class AudioSpeaker
{
public:
void UpdateSong()
{
cout<<" new song playing"<<endl;
}
};
int main() //Client code
{
songs so1;
screen s1;
icon i1;
AudioSpeaker A1;
so1.selectnewsong();
s1.updateScreen();
i1.updateIcon();
A1.UpdateSong();
return 0;
}
In this example, clearly, the client is directly interacting with all objects. Hence, the client is keeping track of all dependent objects and calling update function for all these objects.
Suppose, if in future, more objects get added, then client need to track them also. Any changes in songs application may impact full client logic.
In summary, above design application is not at all open for modification. In order to solve above problems Observer pattern is the best fit.
How
Lets us try to update above application with Observer design pattern. We need to follow below rules for Observer design pattern:
Identify the monitored and observer component
For the above application
- Song Class – Monitored component.
- Screen, Icon and Audio Speaker – Observer Component.
Create the Song monitor and Song Observer classes. Derive all observer component such as Screen, Icon and Audio from Song Observer class.
class SongObserver
{
};
class Screen: public SongObserver
{
};
class Icon : public SongObserver
{
};
class SongMonitor
{
}
Embed all observer objects inside Monitor class
Embed Song Observer objects inside Song Monitor class. Expose attachSongObserver( ) interface for subscribing observer within monitor class.
class SongMonitor
{
vector<SongObserver*> m_so;
public:
void attachSongObserver( SongObserver* so)
};
Implement subscribe in observer class
Try to subscribe Observer Class objects during its initialization.
class SongObserver
{
public:
SongObserver(SongMonitor *sm)
{
sm->attachSongObserver(this);
}
};
Implement notify method in monitored class
The monitored class shall notify or inform to all observer classes. For instance, on new song selection, the notify shall iterate over all song observer objects. Thus, it will call update observer.
void SongMonitor::selectnewsong()
{
cout<<"new song selected"<<endl;
for ( int i = 0; i < m_so.size(); i++ )
{
m_so[i]->updateObserver();
}
}
UML Diagram for observer design pattern
Application using Observer Design Pattern
Below is the updated code for above application with Observer design pattern:
#include<iostream> //main header
#include<vector> //for vector
using namespace std;//for namespace
class SongObserver; //Forward Declaration
class SongMonitor //Publisher
{
vector<SongObserver*> m_so;
public:
void attachSongObserver( SongObserver* so)
{
m_so.push_back(so);
}
void selectnewsong();
};
class SongObserver //Subscriber
{
public:
SongObserver( SongMonitor *sm)
{
sm->attachSongObserver(this);
}
virtual void updateObserver() =0;
};
class screen: public SongObserver
{
public:
screen( SongMonitor *sm):SongObserver(sm)
{
}
void updateObserver()
{
cout<<"Update new song data in Screen"<<endl;
}
};
class icon : public SongObserver
{
public:
icon( SongMonitor *sm):SongObserver(sm)
{
}
void updateObserver()
{
cout<<"New Icon loaded"<<endl;
}
};
class AudioSpeaker: public SongObserver
{
public:
AudioSpeaker( SongMonitor *sm):SongObserver(sm)
{
}
void updateObserver()
{
cout<<" New song playing"<<endl;
}
};
void SongMonitor::selectnewsong()
{
cout<<"new song selected"<<endl;
for ( int i = 0; i < m_so.size(); i++ )
{
m_so[i]->updateObserver();
}
}
int main()
{
SongMonitor *sm = new SongMonitor;
SongObserver *screenObserver = new screen(sm);
SongObserver* iconObserver = new icon(sm);
SongObserver* audioObserver = new AudioSpeaker(sm);
sm->selectnewsong();
delete sm;
delete screenObserver;
delete iconObserver;
delete audioObserver;
return 0;
}
Clearly, now client not longer directly interacts with all the Observer objects. Client is just calling Monitored object select song interface. Therefore, any changes in Observer object will not impact client.
In Summary, if we want to add new object to observe the state, then just initialize it. On initialization it will subscribe to the Monitored object. Therefore, with Observer pattern it is very easy and simple to monitor the objects. Whenever we have such use cases, where we want to monitor and notify state changes, Observer pattern is the best option.
Pros and Cons of Observer Design Pattern
Pros
- Firstly, the addition and deletion of objects is very easy and will not impact client.
- Secondly, the client application remains very simple or less complex.
Cons
- Firstly, we need to design publish-subscribe principle very carefully.
- Any issue in design will impact full application.