Понадобилось мне, чтобы каждый класс мог стать субъектом (subject) и оповещать своих наблюдателей (observers) любыми типами данных.
Чтобы вечно не использовать наследование для разных типов данных, я написал универсальный шаблонный класс.
Для начала определим интерфейсы наблюдателя и субъекта.
ASObserver.h
template <class T>
class ASObserver{
public:
virtual ~ASObserver(){}
virtual void onNotify(T *data) = 0;
};
ASSubject.h
#include "ASObserver.h"
#include <vector>
using std::vector;
using std::remove;
// ObserverClass — тот, кто наблюдает
// ObserverDataClass — тип данных, которым мы будем уведомлять ObserverClass
// с помощью метода notify
template <class ObserverClass, class ObserverDataClass>
class ASSubject{
public:
virtual void addObserver(ASObserver<ObserverDataClass> *observer) = 0;
virtual void removeObserver(ASObserver<ObserverDataClass> *observer){};
protected:
virtual void notify(ObserverDataClass *data) = 0;
};
Теперь реализуем в своих классах методы интерфейса. В файле ASConcreteSubject.tpp реализация шаблонных методов (для удобства, я просто разделил объявление и реализацию).
ASConcreteSubject.h
#include "ASSubject.h"
#include "ASObserver.h"
using std::vector;
using std::remove;
template <class ObserverClass, class ObserverDataClass>
class ASConcreteSubject : public ASSubject<ObserverClass, ObserverDataClass>{
public:
void addObserver(ASObserver<ObserverDataClass> *observer);
void removeObserver(ASObserver<ObserverDataClass> *observer);
protected:
void notify(ObserverDataClass *data);
private:
vector<ASObserver<ObserverDataClass> *> observers;
};
#include "ASConcreteSubject.tpp"
ASConcreteSubject.tpp
#include "ASConcreteSubject.h"
template <class ObserverClass,class ObserverDataClass>
void ASConcreteSubject<ObserverClass, ObserverDataClass>::addObserver(ASObserver<ObserverDataClass> *observer){
observers.push_back(observer);
}
template <class ObserverClass,class ObserverDataClass>
void ASConcreteSubject<ObserverClass, ObserverDataClass>::removeObserver(ASObserver<ObserverDataClass> *observer) {
observers.erase(remove(observers.begin(), observers.end(), observer), observers.end());
}
template <class ObserverClass,class ObserverDataClass>
void ASConcreteSubject<ObserverClass, ObserverDataClass>::notify(ObserverDataClass *data){
for( auto &observer : observers ){
observer->onNotify(data);
}
}
А теперь самое интересное: а как же использовать этот код?
main.cpp
struct mydata_t{ int code; }
class TestSubject : public ASConcreteSubject<Test, mydata_t>{
public:
void onKeyPressed(){
mydata.code = 5;
notify(mydata);
}
private:
mydata_t mydata;
};
class TestObserver : public ASObserver<mydata_t>{
public:
TestObserver(){ testSubject.addObserver(this); }
private:
TestSubject testSubject;
};
Таким образом можно, например, реализовать систему уведомлений, вроде keyboard events.
По нажатию клавиш уведомлять своих наблюдателей, при том, наблюдателем может быть любой класс, а оповещать любыми данными.