Observer_Pattern_2 - 8BitsCoding/RobotMentor GitHub Wiki
-
Getting notification when things happen.
-
An observer is ans object that wishes to be informed about events happening in the system.
-
The entity generating the events is an observable.
class Person : public Observable<Person>
{
int age;
public:
int get_age() const
{
return age;
}
void set_age(int age)
{
if (this->age == age) return;
this->age = age;
notify(*this, "age");
}
};
struct ConsolePersonObserver : public Observer<Person>
{
private:
void field_changed(Person& source, const std::string& field_name) override
{
cout << "Person's " << field_name << " has changed to ";
if(field_name == "age") cout << source.get_age();
cout << "\n.";
}
};
int main()
{
Person person{10};
ConsolePersonOberver cpo;
person.subscribe(cpo);
person.set_age(11);
person.set_age(12);
person.unsubscribe(cpo);
person.set_age(13);
return 0;
}
// Observer
#pragma once
#include <string>
template <typename T>
class Observer
{
virtual void field_changed(T& source, const std::string& field_name) = 0;
};
// Observable
#pragma once
#include <string>
#include <vector>
template <typename> struct Observer;
template <typename T>
class Observable
{
std::vector<Observer<T>*> observers;
public:
void nofity(T& source, const std::string& field_name)
{
for(auto observer : observers)
{
observer->field_changed(source, field_name);
}
}
void subscribe(Observer<T>& observer)
{
observers.push_back(&observer);
}
void unsubscribe(Observer<T>& observer)
{
observer.erase(
remove(osbervers.begin(), observers.end(), oberver),
observers.end()
);
}
};
#pragma once
#include <string>
#include <vector>
#include <mutex>
template <typename> struct Observer;
template <typename T>
struct SaferObservable
{
std::vector<Observer<T>*> observers;
typedef std::recursive_mutex mutex_t;
mutex_t mtx;
public:
void notify(T& source, const std::string& field_name)
{
std::scoped_lock<mutex_t> lock{mtx};
for (auto observer : observers)
if (observer)
observer->field_changed(source, field_name);
}
void subscribe(Observer<T>& observer)
{
std::scoped_lock<mutex_t> lock{mtx};
observers.push_back(&observer);
}
void unsubscribe(Observer<T>& observer)
{
auto it = std::find(begin(observers), end(observers), &observer);
if (it != end(observers))
*it = nullptr;
// std::scoped_lock<mutex_t> lock{mtx};
// observers.erase(
// remove(observers.begin(), observers.end(), &observer),
// observers.end()
// );
}
};
// main.cpp
#include "Headers.hpp"
#include "Observer.hpp"
#include "SaferObservable.hpp"
class Person : public SaferObservable<Person>
{
int age{0};
public:
Person(){}
Person(int age) : age(age) {}
int get_age() const
{
return age;
}
void set_age(int age)
{
if (this->age == age) return;
auto old_can_vote = get_can_vote();
this->age = age;
notify(*this, "age");
if (old_can_vote != get_can_vote())
notify(*this, "can_vote");
}
bool get_can_vote() const {
return age >= 16;
}
};
// observer & observable
struct ConsolePersonObserver
: public Observer<Person>
{
private:
void field_changed(Person &source, const std::string &field_name) override
{
cout << "Person's " << field_name << " has changed to ";
if (field_name == "age") cout << source.get_age();
if (field_name == "can_vote")
cout << boolalpha << source.get_can_vote();
cout << ".\n";
}
};
struct TrafficAdministration : Observer<Person>
{
void field_changed(Person &source, const std::string &field_name) override
{
if (field_name == "age")
{
if (source.get_age() < 17)
cout << "Whoa there, you're not old enough to drive!\n";
else
{
cout << "Oh, ok, we no longer care!\n";
source.unsubscribe(*this);
}
}
}
};
int main(int ac, char* av[])
{
Person p;
TrafficAdministration ta;
p.subscribe(ta);
p.set_age(15);
p.set_age(16);
try
{
p.set_age(17);
}
catch (const std::exception& e)
{
cout << "Oops, " << e.what() << "\n";
}
return 0;
}
// Observable.hpp
#pragma once
#include <string>
#include <vector>
template <typename> struct Observer;
template <typename T>
struct Observable
{
std::vector<Observer<T>*> observers;
public:
void notify(T& source, const std::string& field_name)
{
for (auto observer : observers)
observer->field_changed(source, field_name);
}
void subscribe(Observer<T>& observer)
{
observers.push_back(&observer);
}
void unsubscribe(Observer<T>& observer)
{
observers.erase(
remove(observers.begin(), observers.end(), &observer),
observers.end()
);
}
};
// Observer.hpp
#pragma once
#include <string>
template <typename T>
struct Observer
{
virtual void field_changed(
T& source,
const std::string& field_name
) = 0;
};
// SaferObservable.hpp
#pragma once
#include <string>
#include <vector>
#include <mutex>
template <typename> struct Observer;
template <typename T>
struct SaferObservable
{
std::vector<Observer<T>*> observers;
typedef std::recursive_mutex mutex_t;
mutex_t mtx;
public:
void notify(T& source, const std::string& field_name)
{
std::scoped_lock<mutex_t> lock{mtx};
for (auto observer : observers)
if (observer)
observer->field_changed(source, field_name);
}
void subscribe(Observer<T>& observer)
{
std::scoped_lock<mutex_t> lock{mtx};
observers.push_back(&observer);
}
void unsubscribe(Observer<T>& observer)
{
auto it = std::find(begin(observers), end(observers), &observer);
if (it != end(observers))
*it = nullptr;
// std::scoped_lock<mutex_t> lock{mtx};
// observers.erase(
// remove(observers.begin(), observers.end(), &observer),
// observers.end()
// );
}
};