デザインパターン・Observer(オブザーバ)パターンの実装をご紹介します。
Observerパターンを使用することで、非同期処理の応答を取得することができ、また依存関係の低減に貢献できます。
Observerパターンとは
Observer(オブザーバ)パターンとは、システムの状態を監視し通知するデザインパターンです。
英語では観察者の意味になりますが、観察者というより通知人ですね。
用途・どんな時に使えるの?
主に以下の目的で使用します。
- 非同期要求に対して応答する
- 下位クラスが上位クラスへ通知する
C++やJavaなどクラス継承の概念を持つ言語でよく使われるデザインパターンです。
これらの概念がないC言語ではコールバック関数を用いてオブザーバのようなことをしています。
クラス図
オブザーバのクラス設計図です。
これはstateクラスの状態が変化した時にcontentsクラスへ通知する例です。
まずcontentsはstate_observerを継承し、stateクラスのattach_observerメソッドを用いてstate_observerを登録します。stateクラスの状態が変化すると、登録されたstate_observerのnotify_stateメソッドをコールします。notify_stateがコールされるとcontentsとstate_observerはA is Bの関係であるためオーバーライドされcontentsへ通知されます。
このようにしてstateクラスの状態をstate_observerクラスを経由してcontentsクラスへ通知することができます。
※ExObserverはただのmain関数であり、使用者です。
実装の解説
C++で実装したサンプルコードを解説します。
このサンプルコードは、mainスレッド上でwhileループ待ちし、stateクラスから通知されるSTATUSの状態見てループから抜け出します。
まずcontentsのコンストラクタでstateクラスへthisポインタを登録します。contentsとstate_observerは継承関係(A is B)ですので、thisポインタはstate_observerと同じです。
デストラクタでは不正アクセスやメモリリークの要因にならないよう登録したthisポインタを解除し、delete処理をします。
contents.hでは通知を受け取るメソッドnotify_stateはprivateで定義します。
■ contents.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include "contents.cpp" #include "logout.h" namespace hukusuke { /*--------------------------------*/ /* contents. */ /*--------------------------------*/ /* Constructor */ contents::contents(){ is_completed_ = false; // オブザーバ登録する. state_ = new state(); state_->attach_observer(this); } /* Destructor */ contents::~contents(){ if (NULL != state_) { // オブザーバ解除する. state_->dettach_observer(); // 後処理. delete state_; state_ = NULL; } else {} } /* Operator method. */ void contents::operator()() { process_(); } /* Get completed flag method. */ bool contents::get_completed_flag() { return is_completed_; } /* process method. */ void contents::process_() { if (NULL != state_) state_->process_change_state(); } /* notify state from observer.*/ void contents::notify_state(const STATUS status) { LOG_OUTPUT("%d", status); if (STATUS_END == status) { is_completed_ = true; } else {} } } |
■ contents.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#pragma once #include "stdafx.h" #include "state.h" #include "define_common.h" using namespace std; namespace hukusuke { class contents : public state_observer { public: contents(); ~contents(); /* Method */ void operator()(); // thread—p. bool get_completed_flag(); private: /* Method */ void process_(); void notify_state(const STATUS status); /* Veriable*/ bool is_completed_; state* state_; }; } |
stateクラスでは、オブザーバ登録メソッドattach_observerとオブザーバ解除メソッドdettach_observerを定義します。
attach_observerでは引数のポインタを自身がもっているstate_observerクラスへ設定し、dettach_observerはNULLを設定します。
state.hで継承するstate_observerは、通知用のnotify_stateを純粋仮想関数で定義します。
■ state.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include "state.h" #include "Windows.h" namespace hukusuke { state::state(){ observer_ = NULL; } state::~state(){} /* attach observer method. */ void state::attach_observer(state_observer* obs){ if (NULL == observer_) { observer_ = obs; } else {} } /* dettach observer method. */ void state::dettach_observer() { if (NULL != observer_) { observer_ = NULL; } else {} } /* process_change_state method. */ void state::process_change_state() { if (NULL != observer_) { observer_->notify_state(STATUS_START); Sleep(100); observer_->notify_state(STATUS_PROCESSING); Sleep(100); observer_->notify_state(STATUS_WAITING); Sleep(100); observer_->notify_state(STATUS_END); } else {} } } |
■ state.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#pragma once #include "stdafx.h" #include "define_common.h" using namespace std; namespace hukusuke { class state_observer; // 宣言 /*--------------------------------*/ /* state. */ /*--------------------------------*/ class state { public: state(); ~state(); void attach_observer(state_observer* obs); // オブザーバ登録. void dettach_observer(); // オブザーバ解除. void process_change_state(); private: state_observer* observer_; }; /*--------------------------------*/ /* state_observer. */ /*--------------------------------*/ class state_observer { public: state_observer() {}; virtual ~state_observer() {}; /* Virtual method*/ virtual void notify_state(const STATUS status) = 0; }; } |
■ ExObserver.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// ExObserver.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include "logout.h" #include "contents.h" #include "Windows.h" #include <thread> using namespace std; using namespace hukusuke; /* main function. */ int main(void) { LOG_OUTPUT("Start."); contents cont; // 状態変化の処理開始(非同期). thread t1(ref(cont)); // 状態が完了するまで待つ. while (true) { LOG_OUTPUT("wait..."); if (true == cont.get_completed_flag()) break; Sleep(30); } // スレッドの待ち合わせ. t1.join(); LOG_OUTPUT("Terminate."); return 0; } |
サンプルコード
Visual C++ 2017で作成したサンプルコードをGitHubで公開しています。
GitHub – ExObserver
実行結果は以下の通りです。stateクラスからの通知により、notify_stateでSTATUSが変化していることがわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[text] [ 15] Start. [ 47] 0 [ 25] wait... [ 25] wait... [ 25] wait... [ 25] wait... [ 47] 1 [ 25] wait... [ 25] wait... [ 25] wait... [ 47] 2 [ 25] wait... [ 25] wait... [ 25] wait... [ 47] 3 [ 25] wait... [ 33] Terminate. [/text] |
参考
以下のサイトを参考にさせていただきました。
TECHSCORE(テックスコア) – 17.Observer パターン
サンプルコードがとても、分かりやすかったです。
ありがとうございます。
他のサイトのサンプルだと、挙動が見にくかったのですが、
こちらの記事は、非同期処理を想定したサンプルを記載しており、
使用方法が想像しやすかったです。