【コードあり】オブザーバーパターンのc++版サンプルコード
今回はデザインパターンのオブザーバーパターンのサンプルコードを作成しました。
記事の作成に当たってインプットには以下の書籍やサイトを使って勉強しました。
間違い等あればコメントしてもらえると対応します。
目次
オブザーバーパターンの概要
オブザーバーパターンは、1対多のオブジェクト間での通知システムを実現します。
観察対象(サブジェクト)と観察者(オブザーバー)からなり、観察対象が特定のアクションを行った場合に観察者全員に通知が送られます。
構造はrefactoring.guruの図がわかりやすいのでそれ見て下さい。
c++のサンプルコード
例として、新聞社が購読中の読者にニュースを通知するプログラムを作成しました。
サブジェクトが新聞社、オブザーバーが読者に当たります。
インターフェース(Observer.h)
サブジェクトのNotify関数に呼ばれるupdate関数を実装します。
Notify関数実行時にすべてのObserverのupdate関数がコールされることで通知を実現します。
#include <iostream>
// オブザーバーのインターフェース
class Observer {
public:
virtual void update(const std::string& message) = 0;
};
読者クラス(Reader.h)
実行時に登録・非登録がわかりやすくするためにインスタンス作成時に名前を設定し、通知時に名前が出力されるようにしています。
#include "Observer.h"
// 新聞の読者クラス
class Reader : public Observer {
private:
std::string name;
public:
// コンストラクタで読者名を設定
Reader(const std::string& name) : name(name) {}
// Observerクラスのupdate関数を実装
void update(const std::string& news) override {
std::cout << "Reader " << name << " received news: " << news << std::endl;
}
};
新聞社クラス(NewsPaperCompany.h)
メンバ変数に、オブザーバー登録用のObserver型のvector配列を保持し、引数で受け取ったオブザーバーを配列に追加するSubscribe関数と、引数で受け取ったオブザーバーを配列から削除するUnsubscribe関数を作成します。
また、observers配列のすべてのObserverのupdate関数を実行するNotify関数を実装します。
#include "Observer.h"
#include <vector>
class NewsPaperCompany {
private:
std::vector<Observer*> observers;
public:
// オブザーバーを登録する関数
void Subscribe(Observer* observer) {
observers.push_back(observer);
}
// オブザーバーの登録を解除する関数
void Unsubscribe(Observer* observer) {
for (auto it = observers.begin(); it != observers.end(); ++it) {
if (*it == observer) {
observers.erase(it);
break;
}
}
}
// 登録中のオブザーバーに通知を送る関数
void Notify(const std::string& message) {
for (auto observer : observers) {
observer->update(message);
}
}
};
Main関数(main.cpp)
登録中の人に適切に通知が行っているか確認できるように、登録と登録解除を繰り返し、通知を3回送るようにしています。
#include "NewsPaperCompany.h"
#include "Reader.h"
int main() {
// 新聞社のインスタンス
NewsPaperCompany UltraLeftistNews;
// 読者のインスタンス
Reader reader1("Lenin");
Reader reader2("Marcus");
Reader reader3("Stalin");
Reader reader4("Leonid Brezhnev");
Reader reader5("Noam Chomsky");
Reader reader6("Bernie Sanders");
// 新聞社に読者を登録
UltraLeftistNews.Subscribe(&reader1);
UltraLeftistNews.Subscribe(&reader2);
// 最新ニュースを更新
UltraLeftistNews.Notify("Breaking news: Russian rebolution occurred!");
std::cout << "\n" << std::endl;
// 新聞社に読者を追加登録
UltraLeftistNews.Subscribe(&reader3);
UltraLeftistNews.Subscribe(&reader4);
// 最新ニュースを更新
UltraLeftistNews.Notify("Breaking news: Spanish civil war occurred!");
std::cout << "\n" << std::endl;
// 読者を登録解除
UltraLeftistNews.Unsubscribe(&reader1);
UltraLeftistNews.Unsubscribe(&reader2);
// 新聞社に読者を追加登録
UltraLeftistNews.Subscribe(&reader5);
UltraLeftistNews.Subscribe(&reader6);
// 最新ニュースを更新
UltraLeftistNews.Notify("Breaking news: Vietnam war occurred!");
return 0;
}
サンプルコードの実行結果
プログラムを実行すると以下のようになります。
一度目のNotifyで登録中のreader1とreader2に通知が行き、二度目のNotifyで追加で登録したreader3,reader4にも通知が届くようになり、三度目のNotifyで登録解除したreader1とreader2には通知が行かず、新たに登録したreader5とreader6に通知が行くようになっています。
登録と登録解除が適切に行えていることがわかります。
Reader Lenin received news: Breaking news: Russian rebolution occurred!
Reader Marcus received news: Breaking news: Russian rebolution occurred!
Reader Lenin received news: Breaking news: Spanish civil war occurred!
Reader Marcus received news: Breaking news: Spanish civil war occurred!
Reader Stalin received news: Breaking news: Spanish civil war occurred!
Reader Leonid Brezhnev received news: Breaking news: Spanish civil war occurred!
Reader Stalin received news: Breaking news: Vietnam war occurred!
Reader Leonid Brezhnev received news: Breaking news: Vietnam war occurred!
Reader Noam Chomsky received news: Breaking news: Vietnam war occurred!
Reader Bernie Sanders received news: Breaking news: Vietnam war occurred!