2010年5月9日日曜日

Singletonパターン

Singletonパターンとは、システム内でとあるクラスのインスタンスを「たった1つ」しか作らない(作りたくない)ようにするためのパターンです。このパターンを実現することで、指定したクラスのインスタンスが『絶対に』1つしか無いことを保証します。

Singletonにするクラスは以下のようになります。
public class SingletonSample {
    private static SingletonSample instance = new SingletonSample();

    /**
     * デフォルトコンストラクタ
     * 外部から使用されないようにprivateにしている
     */
    private SingletonSample() {}

    public static SingletonSample getInstance() {
        return instance;
    }
}
このクラスのように、Singletonを実現する場合は
  • 自身のインスタンスを持つprivateフィールドを用意する。
  • デフォルトコンストラクタにprivateをつける。
  • インスタンスを取得するメソッドを用意する。
となります。

Mainクラス(SingletonSampleクラスを利用するクラス)では以下のようにgetInstance()を呼べば良いです。
SingletonSample obj1 = SingletonSample.getInstance();

ちなみに、以下のようにMainクラスでSingletonSampleインスタンスを作るコードを入れると、
SingletonSample obj1 = new SingletonSample();




上の通り、コンパイルエラーになります。デフォルトコンストラクタがprivateで外部から利用できないようにしているので当たり前ですね。

また、getInstance()を何度呼んでも同じインスタンスなのかをチェックしてみます。以下のように、getInstance()を2回呼んで、それぞれの変数(obj1とobj2)は同じインスタンスを指しているのかをチェックします。(オブジェクト型を==で比較した場合、それは同一のインスタンスかどうか、正確に言うと、そのインスタンスが置かれているメモリ上の番地が同じかをチェックします。番地が同じならば同じインスタンスを指しているということになり、trueと判定されます)

SingletonSample obj1 = SingletonSample.getInstance();
    SingletonSample obj2 = SingletonSample.getInstance();
    if (obj1 == obj2) {
        System.out.println("same");
    } else {
        System.out.println("different");
    }
実行結果は以下の通りです。



Singletonパターンの悪い実装例も挙げておきます。
    private static SingletonSample instance;

    /**
     * デフォルトコンストラクタ
     * 外部から使用されないようにprivateにしている
     */
    private SingletonSample() {}

    public static SingletonSample getInstance() {
        if (instance == null) {
            instance = new SingletonSample();
        }
        return instance;
    }

上記では、SingletonSampleのインスタンスをフィールドで生成するのではなく、getInstance()が呼ばれた際にnullであるかどうかを判定して、もしnullならばその時に生成する方法を取っています。がしかし、複数の異なるスレッドから「ほぼ同時に」getInstance()を呼び出した場合、SingletonSampleのインスタンスが複数生成される可能性があります。

Javaでは、クラスに初めてアクセスした際にそのクラスの初期化が行われる(当然、その初期化は最初の1回のみ)ので、フィールドでインスタンスを生成するようにしておけば、複数生成されることは無いということになります。

注意:悪い実装例で、getInstance()にsynchronizedをつける方法も考えられるが、同期化コストが高いので推奨しません(と、wikipediaに書かれている)。スレッドのことをよく知らないので、そのコストがどれだけ高いのか分かりません(´・ω・`)

関連パターン
以下のパターンはインスタンスが1つである場合が多い

0 件のコメント:

コメントを投稿