观察者模式传统上需要观察者实现
update()
方法。
但是,当我们有 lambdas 时,我们也可以这样做:
#include <functional>
#include <vector>
#include <iostream>
class Subject
{
public:
using CallbackClosure = std::funtion<void()>;
void RegisterCallback(CallbackClosure&& cb) {
closures_.push_back(std::move(cb));
}
void test() {
for (auto const& cb : closures_) {
cb();
}
}
private:
std::vector<CallbackClosure> closures_;
};
// usage
int main()
{
Subject s;
// register here with a lambda instead of a pointer to the observer
s.RegisterCallback([]{ std::cout << "Hey from main\n"; }
s.test();
return 0;
}
我知道我们当然可以让
RegisterCallback
成为一个ure virtual并让不同的主题来实现。那个界面,但上面的内容应该足以说明这个例子了。
这是实现“观察者模式”的现代方式吗?或者为观察者定义更新接口也有一些好处吗? lambda 可以简单地被视为匿名观察者吗?
#include <unordered_map>
#include <functional>
#include <iostream>
namespace details
{
struct revoke_itf_t
{
virtual void revoke(std::size_t cookie) = 0;
virtual ~revoke_itf_t() = default;
};
struct subscription_t
{
std::size_t cookie;
revoke_itf_t* subscriptions;
// todo bool flag and move constructor
~subscription_t()
{
subscriptions->revoke(cookie);
}
};
}
template<typename... args_t>
class callbacks_t :
public details::revoke_itf_t
{
public:
[[nodiscard]] auto subscribe(std::function<void(args_t...)> callback)
{
m_subscription_id++;
m_subscriptions.insert({m_subscription_id,callback});
return details::subscription_t{m_subscription_id,this};
}
void operator()(args_t&&... args)
{
for(auto& [cookie,callback] : m_subscriptions)
{
callback(std::forward<args_t>(args)...);
}
}
private:
void revoke(std::size_t cookie) override
{
auto it = m_subscriptions.find(cookie);
if ( it != m_subscriptions.end() )
{
m_subscriptions.erase(it);
}
}
std::size_t m_subscription_id{0ul};
std::unordered_map<std::size_t,std::function<void(args_t...)>> m_subscriptions;
};
int main()
{
callbacks_t<int> subject;
{
auto subscription = subject.subscribe([](int value) { std::cout << value << "\n"; });
subject(1);
subject(2);
// subscription goes out of scope here
}
subject(3);
}