シングルトンパターン

デザインパターンとは

デザインパターンとは、偉大な先人たちがまとめたクラス(間)設計集のことであり、 これを参考にすることで初心者でもきれいな設計が出来、 オブジェクト指向の勉強にもなります。

ここでは最も有名なGoFの23個のデザインパターンの一つを説明し、 他の似たパターンとの比較もします。

シングルトンパターン(Singletonパターン)

シングルトンパターンの目的

C++では通常のクラスはコンストラクタの呼び出し(newの使用や非ポインタ、非参照変数の宣言など) 毎に実体が生成されます。

しかしログや重いオブジェクトなど、プログラム実行中に実体は一つのみ生成し、その実体を使いまわしたい場合があります。

この様な場合にシングルトンパターンを適用すると、 実体がプログラム中で一つであることが保証されます。

シングルトンパターンの実装(Java風)

シングルトンパターンを(Java風に)C++で実装するには次のようにします。

これでクラスにシングルトンパターンを適用したことになりますが、 コピーコンストラクタと代入演算子については説明がまだですのでここでは実装しません。

 class SingletonClass {
 private:
     SingletonClass(void) {}
     static SingletonClass* singleton;
 public:
     static SingletonClass* getInstance(void) {
         return singleton;
     }
     void start(void);
     bool doSomething(int something);
     bool end(void);
 };

 SingletonClass* SingletonClass::singleton = new SingletonClass();

このようにする事で、外部から実体は生成できず、 唯一の実体であるsingletonを得ることの出来る関数getInstanceを介してでしかメンバ関数にアクセス出来なくすることが可能です。

このクラスの使用例は次の通りです。

 int main(void) {
     SingletonClass* sc = Singleton::getInstance();
     sc->start();
     sc->doSomething(1);
     sc->end();
 }

シングルトンパターンの実装(C++風)

今度はC++風に実装してみましょう。

 class SingletonClass {
 private:
     SingletonClass(void) {}
 public:
     static SingletonClass& getInstance(void) {
         static SingletonClass singleton;
         return singleton;
     }
     void doSomething(void);
 };
 

これは先ほどの例とはアプローチが異なります。

コンストラクタ類とメンバ変数をprivateにして生成を防ぐのが先の実装のコンセプトでしたが、この例ではメンバ変数として自身のオブジェクトを持つのではなく、getInstance関数の中にstaticで実体の生成を行っています。

これによってgetInstance関数の初回呼び出し時にSingletonオブジェクトが生成され、初回以降はそのオブジェクトが使いまわされます。

モノステイト(モノステート)パターン

モノステイトパターンはシングルトンパターンと似たパターンですが、 GoFのデザインパターンの中には含まれていません。

C++で実装するにはコンストラクタ系をprivateにし、 メンバを全てstaticにします。 こちらはシングルトンパターンよりも簡単に思いつくパターンでしょう。

以下に例を示します。

 class MonostateClass {
 private:
     MonostateClass(void);
 public:
     static double sin(double rad);
     static double cos(double rad);
     static double tan(double rad);
 };

このようにする事で、"MonostateClass::関数名" という方法でしかこのクラスの関数にアクセスすることは不可能になります。

しかしC++ではモノステイトパターンをクラスで実装する必要はなく、無名名前空間を使用すれば同じようなことが出来ます。

これは自分で考えてみてください。

ヒントはヘッダと実装ファイル、名前空間に無名名前空間です。

シングルトンパターンとモノステイトパターンの相違点

シングルトンパターンとモノステイトパターンでは実体が2つ以上作られることはありません。 ではこの2つのパターンはどちらを使っても良いのでしょうか? ここではその疑問を解決します。

ポリモーフィズムの有無

相違点の一つ目は、シングルトンパターンではポリモフィズムが使用でき、モノステイトパターンでは使用出来ない点です。

これは当たり前の話で、staticなメンバ関数はvirtualに出来ません。 しかもこの2つのパターンはどちらもコンストラクタ系をprivateにすることで、 ユーザが勝手に実体生成できないようにしているのですが、 その所為でモノステイトパターンでは自身のポインタを得ることが出来ません(シングルトンパターンではgetInstance関数で可能)。 つまりモノステイトパターンではポリモーフィズムの使用は不可能です。

呼び出しの容易性

二つ目は、呼び出しの容易性です。

シングルトンパターンでメンバ関数を呼び出すには、次の2パターンあります。

 クラス名 変数名 = クラス名::getInstance();
 変数名.メンバ関数名();
 クラス名::getInstance().メンバ関数名();

どちらにしてもgetInstance関数を呼び出さなければなりませんね。

これに対しモノステイトパターンでは"クラス名::関数名"で呼び出せました。

 クラス名::メンバ関数名();

明らかにモノステイトパターンのほうが呼び出しは容易ですね。

結論

他にも相違点はまだまだありますが、大きいものはこの2つでしょう。

このことから、モノステイトパターンは処理が不変な関数群からなるクラス(ユーティリティクラス) に、

シングルトンパターンは汎用性、可搬性が必要なクラスに使用すべきでしょう。

モノステイトパターンは元々「状態が不変」であることを表すパターンだと思うので、 「実体が一つであることを保証する」といった使い方には適さないでしょう。

他の相違点は自分で考えてみてください。