デザインパターン「Singleton」


はじめに

本投稿はJava言語で学ぶデザインパターン入門のデザインパターンをまとめた記事です。今回はSingletonパターンになります。
まとめ一覧はこちら

Singletonパターン

Singletonパターンとは

一言でいうと、生成するインスタンスの数を1つに制限するデザインパターンです。

  • 指定したクラスのインスタンスが1つしか存在しないことを保証する
  • インスタンスが1個しか存在しないことをプログラム上で表現したい

このような場合にSingletonパターンを用いてインスタンスを生成します。
インスタンスの状態を保持したかったり、クラス間で共通のメソッド・プロパティにアクセスしたい場合に使われることが多いですね。

Singletonパターンのクラス図

  • クラス変数としてsingletonを定義
  • インスタンスを得るためのstasticメソッドで同じインスタンスを返す

具体例

生成したインスタンスが同一かどうか簡単な例で確かめてみましょう。

Mainクラス

以下のようにgetInstance()によって得たインスタンスが同一のものか確かめてみます

Main.java
public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        if (instance1 == instance2) {
            System.out.println("instance1 and instance2 are same Instance");
        } else {
            System.out.println("instance1 and instance2 aren't same Instance");
        }       
}

Singletonクラス

Singleton.java
public class Singleton {

    //①
    private static Singleton singleton = new Singleton();
    //②
    private Singleton() {
        System.out.println("Cretae Instance");
    }
    //➂
    public static Singleton getInstance() {
        return singleton;
    }
}

Singletonパターンを適応するにあたり、以下のルールに準拠する必要があります

  • ①自身の型のインスタンスが privateなクラス変数として定義されている
  • ②外部から生成されないようにprivateにしている
  • ➂インスタンスを返すためのクラス関数が定義されている

①のstaticフィールドによってクラス初期化時に唯一のインスタンスが生成されるので、どこからでも参照できるグローバル領域で管理されるんですね。

実行結果

Cretae Instance
instance1 and instance2 are same Instance

一回のみコンストラクタが呼ばれていますね。

Mainクラスでテストしたように、Singletonクラスは利用する側からは

  • newできない
  • 同一なオブジェクトが返される

クラスであると言えます

マルチスレッドに対応した例

実際のシステムでは複数のスレッドに同時にアクセスする場合があり、想定しないインスタンスが生成される場合があります。
その場合は、インスタンスを返すgetInstace()のメソッドをsynchronized指定し、同期処理をさせる必要があります。

Singleton.java
class Singleton {
    static Singleton instance = null;
    public static synchronized Singleton getInstace() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

この場合は、同期処理による処理速度の低下を考慮する必要があります。

2016/11/4更新

現在ではこのような記述が推奨されているようです。

@sipadan2003 さんご教授ありがとうございます。
class Singleton {
    private Singleton(){}

    public static Singleton getInstace() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

  1. Singletonクラスがロードされた時点では、Singletonインスタンスは生成されない
  2. Singleton#getInstance()を最初に呼び出した時に、SingletonHolderクラスがロードされ、Singletonインスタンスが生成される

synchronizedを使用することなく、ギリギリまでSingletonインスタンスを生成しないような挙動になるようです。

iOS(Objective-C)の場合

Singleton.m
@implementation Singleton

static Singleton *sharedInstance = nil;

+ (Singleton *)getInstace
{
    @synchronized(self){
        if (!sharedInstance) {
            sharedInstance = [Singleton new];
        }
    }
    return sharedInstance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        //初期化処理
    }
    return self;
}

Objective-Cの場合も@synchronized用いてスレッドセーフな記述が可能です。
また、dispatch_once_tを用いた記述も可能ですが、

- (void)destructor
{
    sharedInstance = nil;
}

のようなSingleton解放の処理はできないようなので@synchronized用いたスレッドセーフな記述が良さそうです

参照: iOSのシングルトンの話

追記

問題点

Singletonクラスを明示的に開放するための機能がない、もしくは自作で適切なタイミングで解放する必要がある
詳細: シングルトンのベターな実装方法

クラス間の依存関係や状態が一見してわからないので、コードから推測するのが困難
ユニットテストのモック運用や状態設定が困難になる

サンプルコードについて

以下のレポジトリにソースコードをアップしてあります。

shoheiyokoyama/design-pattern_java

デザインパターン

参考文献