デザインパターン・Singleton(シングルトン)パターンの実装をご紹介します。
Singletonパターンを使用することで、ネットワークやシステム内の状態などを常に監視することができます。
Singletonパターンとは
Singleton(シングルトン)パターンとは、
インスタンスの存在を唯一に保証するためのデザインパターンです。
Singletonクラスのインスタンスは何度生成されても1つしか存在せず、プロセスが終わる(製品の電源が切られる)まで存在し続けます。
用途・どんな時に使えるの?
主に以下の目的で使用します。
- システムの状態を常に監視
- インスタンスの管理
- リクエストの窓口(Facadeパターンで使用。この記事で使っています。)
これらの目的を果たすために、Singletonクラスを使用します。
また他のデザインパターンであるFlyweightパターンやFacadeパターンでも使用されています。
クラス図
Singletonのクラス設計図です。
ex_singletonクラスがSingletonクラスです。
privateメンバに自身のポインタとスレッドセーフを考慮してmutexを持ちます。publicメソッドには生成するためのget_instance()メソッドを定義します。
使用者はget_instance()メソッドをコールしてSingletonクラスを使用します。
※ExSingletonはただのmain関数であり、使用者です。
実装の解説
C++で実装したサンプルコードを解説します。
このサンプルコードでは3つのスレッドからSingletonクラスをコールし、Constructor(コンストラクタ)が一度だけ呼ばれていることを確認します。
まずget_instance()内がSingletonパターンのロジックになります。
スレッドセーフを考慮してダブルチェックロッキング(Double-Checked Locking)を採用しています。これはmutex(ミューテックス)ガードを入れることで排他制御を行い、複数のスレッドからアクセスしても問題が発生しないようにすることです。その後、ex_singletonをヒープ領域へ生成し、そのポインタをprivateで持っていたex_singletonへ設定します。
最後にそのインスタンスの実体を参照渡しでreturnします。
ロジックからもわかるように、get_instance()が何回呼ばれても一度生成したex_singletonのインスタンスを返すようになっています。
ヘッダーファイルでは唯一の存在を保証するために必要なメンバをstaticで宣言します。
コンストラクタとデストラクタはprivateで宣言します、御作法といったところでしょうか。
■ ex_singleton.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 |
#include "ex_singleton.h" #include "logout.h" namespace hukusuke { ex_singleton* ex_singleton::instance_ = NULL; mutex ex_singleton::mtx_; /* Constructor */ ex_singleton::ex_singleton() { LOG_OUTPUT("Constructor"); } /* Destructor. */ ex_singleton::~ex_singleton() { LOG_OUTPUT("Destructor"); } /* Get singleton instance. */ ex_singleton& ex_singleton::get_instance() { if ( !instance_ ){ lock_guard<std::mutex> grd(mtx_); // Double-Checked Locking if ( !instance_ ){ ex_singleton* obj = new ex_singleton(); instance_ = obj; } else {} } else {} return *instance_; } /* Start function. */ void ex_singleton::start(){ LOG_OUTPUT("Singleton class start"); } } // namespace hukusuke. |
■ ex_singleton.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 39 40 41 |
#ifndef _EX_SINGLETON_H_ #define _EX_SINGLETON_H_ #include "stdafx.h" #include <mutex> using namespace std; namespace hukusuke { /* ex_singleton class*/ class ex_singleton { /*--------------------------------*/ /* Public method. */ /*--------------------------------*/ public: // Get singleton instance. // @return not NULL on instance, NULL on error. static ex_singleton& get_instance(); void start(); /*--------------------------------*/ /* Private method. */ /*--------------------------------*/ private: // Constructor. ex_singleton(); ex_singleton( const ex_singleton& ); // default. ex_singleton& operator=( const ex_singleton& ); // default. // Destructor. ~ex_singleton(); /*--------------------------------*/ /* Private member. */ /*--------------------------------*/ private: static ex_singleton* instance_; static mutex mtx_; }; } #endif /* _EX_SINGLETON_H_ */ |
Singletonクラスを使用する場合は下記のようになります。
使用者は生成したインスタンスのアドレスを取得することでインスタンスのメソッドを使用することができます。
■ ExSingleton.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 |
// ExSingleton.cpp #include "stdafx.h" #include <thread> #include "ex_singleton.h" #include "logout.h" using namespace std; using namespace hukusuke; /* シングルトンをコールする処理. */ void work_process() { // ログ出力 LOG_OUTPUT("work_process thread_id[%d]", this_thread::get_id()); // シングルトンクラスを呼び出す. ex_singleton& tmp = ex_singleton::get_instance(); tmp.start(); } /* main function. */ int main(void) { LOG_OUTPUT("Start."); // Create thread. thread t1(work_process); thread t2(work_process); thread t3(work_process); // Wait thread work process. t1.join(); t2.join(); t3.join(); LOG_OUTPUT("Terminate."); return 0; } |
サンプルコード
Visual C++ 2017で作成したサンプルコードをGitHubで公開しています。
GitHub – ExSingleton
実行結果(一部省略)は以下の通りです。
[ 26] Start. [ 16] work_process thread_id[26972] [ 13] Constructor [ 39] Singleton class start [ 16] work_process thread_id[26976] [ 39] Singleton class start [ 16] work_process thread_id[26980] [ 39] Singleton class start [ 38] Terminate.
実行結果より、Constructor(コンストラクタ)が一度だけ呼ばれていることが確認できます。またデストラクタは呼ばれていないため、唯一の存在が保証されていることを確認できます。
参考
以下のサイトを参考にさせていただきました。
Qiita – デザインパターン「Singleton」
新品価格 |