Observer design pattern for beginners.
Problem statement –
Nowadays we all are very active on the internet. Many times we
see educational web sites offering free learning with lots of topics.
Different people will be interesting on various topics. And web site has
millions of users. Now, if you think to design such kind software. How you will
keep track of who likes which topics? If u apply simple math, billion users, one
user can love as much topic as he/she wish to learn. And you have to send them
regular updates related to those topics. Like new chapter has been added or
modified on that topic. To all enrolled learners. User very from 1 to 1 million from topic to topic.
Actually we can address this problem through observer design pattern, we will see how in this article.
Actually we can address this problem through observer design pattern, we will see how in this article.
Prerequisite –
All users shall have one unique identity. For example, email
IDs, or say roll numbers, or employee ID, etc. All shall be uniquely identified.
You must have noticed. To register in any online forum, it ask your email ID. The reason is, all they are using is the observer design pattern. So once any update
happens, they can send updates to all registered users.
Why we need observer pattern –
It is nearly impossible to solve the problem as mentioned above
without observer. There are many design patterns exist, which make designers' life easy. But if you don’t use them, you can still survive through implementing project
one or another way, but you will pay huge maintenance cost at the end.
But if there are some problem statements involving massive
participation of different users and many topics. Not only in learning portals
but in social networking sites, where you can be part of many groups. According
to your interest. You are seeking update messages. As soon as anyone
updates something on the registered groups. Then the only way to solve that complex
problem, straightforwardly, is the observer design pattern.
About observer design pattern –
Observer design pattern works in implicit
referencing. Implicit referencing – means; without knowing who the user is, and other information. It broadcast message to all registered users.
Some
people consider that observer works as mediator or kind of extension to mediator pattern. Forwarding messages to its proper
destination, but when the number of users is in millions or in billion. Then we
cannot use the mediator as we have to manually register all of them in the mediator class. And
especially in this case where one person can be part of many topics. Complexity will go high, and it will become unmanageable code.
There are two ways of broadcasting messages in observer pattern –
- Parameterized – Where you pass full message to all registered users.
- Non-parameterized – Where you just send a link to the user. Mentioning that there are some updates, if you wish, please click on it.
Most broadcast and multicast models are designed based on observer design pattern.
#include <iostream> #include <map> #include <set> #include <string> using namespace std; typedef enum _SUBJECTS { CPP, DESIGN } SUBJECTS; const char* SubjetcsList[] = { "CPP", "DESIGN" }; struct UserProfile { string Name; string ID; string MobileNumber; set<SUBJECTS> Subjetcs; UserProfile(string& name, string& id, string& number): Name(name), ID(id), MobileNumber(number) {} }; class Learn { public: virtual void subscribe(shared_ptr<UserProfile> user) = 0; virtual void unSubscribe(string emailId) = 0; virtual void notify(string& user) = 0; virtual void updateContent(string& user, string& msg) = 0; }; class Observer { public: virtual void update(string& id, string& msg) = 0; }; shared_ptr<Observer> GetObserver(SUBJECTS subject); class CPlusPlus : public Learn { private: map<string, shared_ptr<UserProfile> > mObservers; string CppContent; public: CPlusPlus(): CppContent("Welcome to CPP learning") {} void subscribe(shared_ptr<UserProfile> user) { mObservers.insert(pair<string, shared_ptr<UserProfile> >(user->ID, user) ); user->Subjetcs.insert(CPP); } void unSubscribe(string emailId) { auto pSubscriber = mObservers.find(emailId); if (pSubscriber != mObservers.end()) { mObservers.erase(pSubscriber); } else { cout << "This subscriber does not exist " << endl; } } void notify(string& fronUser) { for (auto& observer : mObservers) { if(observer.first != fronUser) // don't send update to self. GetObserver(CPP)->update(observer.second->ID, CppContent); } } void updateContent(string& user, string& msg) { cout << "User " << user << "has updated CPP materials " << endl; CppContent = msg; notify(user); } }; class Design : public Learn { private: map<string, shared_ptr<UserProfile> > mObservers; string DesignContent; public: Design(): DesignContent("Welcome to Design learning") {} void subscribe(shared_ptr<UserProfile> user) { mObservers.insert(pair<string, shared_ptr<UserProfile> >(user->ID, user)); user->Subjetcs.insert(DESIGN); } void unSubscribe(string emailId) { auto pSubscriber = mObservers.find(emailId); if (pSubscriber != mObservers.end()) { mObservers.erase(pSubscriber); } else { cout << "This subscriber does not exist " << endl; } } void notify(string& fronUser) { for (auto& observer : mObservers) { if (observer.first != fronUser) // don't send update to self. GetObserver(DESIGN)->update(observer.second->ID, DesignContent); } } void updateContent(string& user, string& msg) { cout << "User " << user << " has updated Design materials " << endl; DesignContent = msg; notify(user); } }; class CppObserver : public Observer { public: virtual void update(string& id, string& msg) { cout<< "Hi " << id << " check out new update on CPP - " << endl; cout << msg << endl; } }; class DesignObserver : public Observer { public: virtual void update(string& id, string& msg) { cout << "Hi " << id << " check out new update on Design" << endl; cout << msg << endl; } }; namespace { shared_ptr<Learn> pCpp; shared_ptr<Learn> pDesign; shared_ptr<Observer> pCppObserver; shared_ptr<Observer> pDesignObserver; void Setup() { pCpp = make_shared<CPlusPlus>(); pDesign = make_shared<Design>(); pCppObserver = make_shared<CppObserver>(); pCppObserver = make_shared<DesignObserver>(); } } shared_ptr<Observer> GetObserver(SUBJECTS subject) { if (subject == CPP) return pCppObserver; else if (subject == DESIGN) return pCppObserver; } shared_ptr<Learn> GetSubject(SUBJECTS subject) { if (subject == CPP) return pCpp; else if (subject == DESIGN) return pDesign; } void EnrollForCPP(shared_ptr<UserProfile> user) { GetSubject(CPP)->subscribe(user); } void EnrollForDesign(shared_ptr<UserProfile> user) { GetSubject(DESIGN)->subscribe(user); } void Edit(shared_ptr<UserProfile> user, SUBJECTS subject, string content) { auto user_subject = user->Subjetcs.find(subject); if (user_subject == user->Subjetcs.end()) { cout << user->Name << " ! You have not subscribed subject - " << SubjetcsList[subject] << endl; return; } GetSubject(subject)->updateContent(user->ID, content); } shared_ptr<UserProfile> CreateUserProfile(string& name, string& id, string& number) { return make_shared<UserProfile>(name, id, number); } /* ####################################################################################### ######### __ __ _ ###################################################### ######### | \/ | __ _(_)_ __ ###################################################### ######### | |\/| |/ _` | | '_ \ ###################################################### ######### | | | | (_| | | | | | ###################################################### ######### |_| |_|\__,_|_|_| |_| ###################################################### ####################################################################################### */ int main(int argc, char** argv) { Setup(); string name = "Bob"; string id = "Bob@gmail.com"; string number = "1234561"; shared_ptr<UserProfile> user1 = CreateUserProfile(name, id, number); EnrollForCPP(user1); EnrollForDesign(user1); string name_2 = "Max"; string id_2 = "Max@gmail.com"; string number_2 = "1234562"; shared_ptr<UserProfile> user2 = CreateUserProfile(name_2, id_2, number_2); EnrollForDesign(user2); Edit(user1, CPP, "CPP 11 content has been added now"); Edit(user1, DESIGN, "New currency design pattern has been added, check the web-page"); Edit(user2, DESIGN, "New design pattern of pattern oriented software architecture has been added, check the web-page"); Edit(user2, CPP, "CPP 14 content has been added now"); return 0; } ///////////////////////////////////////////////////////////////////-------------OutPut /* User Bob@gmail.comhas updated CPP materials User Bob@gmail.com has updated Design materials Hi Max@gmail.com check out new update on Design New currency design pattern has been added, check the web-page User Max@gmail.com has updated Design materials Hi Bob@gmail.com check out new update on Design New design pattern of pattern oriented software architecture has been added, check the web-page Max ! You have not subscribed subject - CPP */
Thanks for reading it. To learn more about design patterns and basic design principles, please see my web page.
Comments
Post a Comment