テンプレートの実装
C++のテンプレートを勉強し始めたので何か作ってみたいと思い、こちらを参考にしてテンプレートでObserverパターンを実装してみました。
記事のコードでは1種類の通知しかできませんが、キーを使うことで複数種類の通知ができるようにしてみました。
コードは以下です。
#include <vector>
namespace mylib {
// private base class
template <class T>
class observerBase {
private:
virtual bool shouldNotify(const std::string& key) = 0;
virtual void update(T* sender, const std::string& key) = 0;
template <class F>
friend class observable;
};
// observer
template <class T, const std::string&... keys>
class observer : public observerBase<T> {
public:
virtual void update(T* sender, const std::string& key) = 0;
private:
std::vector<std::string> observeKeys = { keys... };
bool shouldNotify(const std::string& key) {
return (std::find(observeKeys.begin(), observeKeys.end(), key) != observeKeys.end());
}
};
// observable
template <class T>
class observable {
public:
void addObserver(observerBase<T> *observer) {
m_observers.push_back(observer);
}
void removeObserver(observerBase<T> *observer) {
auto it = std::find(m_observers.begin(), m_observers.end(), observer);
if (it != m_observers.end())
m_observers.erase(it);
}
void notify(const std::string& key) {
for (const auto& o : m_observers) {
if (o->shouldNotify(key))
o->update(static_cast<T*>(this), key);
}
}
private:
std::vector<observerBase<T>*> m_observers;
};
}
使い方
使い方の例です。継承を使います。
#include <iostream>
#include "observable.h"
// 通知を送るクラス
struct Sender : public mylib::observable<Sender>
{
// Senderはkey1とkey2の2種類の通知をする
static const std::string key1;
static const std::string key2;
// doSomething()で通知
void doSomething() {
notify(key1);
notify(key2);
}
};
const std::string Sender::key1 = "key1";
const std::string Sender::key2 = "key2";
// 通知を受け取るクラス
struct Observer : public mylib::observer<Sender, Sender::key1>
{
Observer() {
auto sender = new Sender();
sender->addObserver(this); // Observer登録
sender->doSomething();
}
// 通知を受け取ると呼ばれる
void update(Sender* sender, const std::string& key) override {
std::cout << "received!" << std::endl;
}
};
int main(int argc, const char * argv[])
{
Observer();
return 0;
}
これでreceived!と表示されます。
おわりに
cocos2d-xを触っていて、EventDispatcherみたいにグローバルにブロードキャストするような通知を多用するのもなんだかなと思いつつ、std::functionに代入すると1対1の通知しかできないので、サクッとObserverパターンを適用できたら便利ではと思い試行錯誤していたところ記事を発見し、テンプレートと継承を組み合わせるのが面白いな〜と感じたので真似してみました。
若干トリッキーなのにもかかわらず、「どのクラスの何を監視するか」が見やすいのがいいですね。
まだC++歴が浅いのでツッコミしてもらえるとありがたいです!