當對象間存在一對多的依賴關係時,當一個對象被修改時,則會自動通知所有依賴於它的對象。這種情況就可以使用觀察者模式。本文的觀察者模式消除了傳統的觀察者模式中觀察者和主題的需要繼承的強耦合關係,可以註冊任意參數的函數,靈活性更高。
#include <unordered_map>
#include <string>
#include <functional>
#include <iostream>
template<typename Callable>
class Subject {
public:
Subject() : observer_id_{0} {}
~Subject() {}
int Attach(Callable&& f) {
return Add(f);
}
int Attach(const Callable& f) {
return Add(f);
}
int Remove(int key) {
return observers_.erase(key);
}
template<typename...Args>
std::unordered_map<int, int> Update(Args&&...args) {
std::unordered_map<int, int> rets;
for (auto const& observer : observers_) {
rets.emplace(observer.first,
observer.second(std::forward<Args>(args)...));
}
return rets;
}
private:
template<typename F>
int Add(F&& f) {
int ret = observer_id_;
observers_.emplace(ret, std::forward<F>(f));
++observer_id_;
return ret;
}
int observer_id_;
std::unordered_map<int, Callable> observers_;
};
class Task1 {
public:
int Add(const int a, const int b) {
std::cout << "do task1" << std::endl;
return a+b;
}
};
class Task2 {
public:
int operator()(const int a, const int b) {
std::cout << "do task2" << std::endl;
return a*b;
}
};
int main() {
Subject<std::function<int(int, int)>> subject;
Task1 task1;
auto task1_key = subject.Attach(
std::bind(&Task1::Add, &task1, std::placeholders::_1, std::placeholders::_2));
Task2 task2;
auto task2_key = subject.Attach(task2);
auto lambda_key = subject.Attach([](int a, int b){
std::cout << "do lambda" << std::endl;
return a -b;});
auto ret = subject.Update(10, 5);
std::cout << "task1 ret:" << ret[task1_key] << std::endl;
std::cout << "task2 ret:" << ret[task2_key] << std::endl;
std::cout << "lambda ret:" << ret[lambda_key] << std::endl;
subject.Remove(lambda_key);
subject.Update(10, 5);
return 0;
}
class內部成員observers_是一個unordered_map,用來保存觀察者註冊的消息,這個消息是一個泛型函數,可以通過key來找到對應的消息。這個key主要用來區分消息,實現保存消息的返回值以及移除消息。
在使用的時候,觀察者只需要調用Attach將函數添加進來,同時保存key。添加的函數是可調用的類型,示例中使用了std::bind和成員函數有operator()的對象,以及lambda表達式。有更新的時候只需要調用Update即可執行所有註冊的函數。