はじめに
本投稿はJava言語で学ぶデザインパターン入門のデザインパターンをまとめた記事です。今回はSingletonパターンになります。
まとめ一覧はこちら
Singletonパターン
Singletonパターンとは
一言でいうと、生成するインスタンスの数を1つに制限するデザインパターンです。
- 指定したクラスのインスタンスが1つしか存在しないことを保証する
- インスタンスが1個しか存在しないことをプログラム上で表現したい
このような場合にSingletonパターンを用いてインスタンスを生成します。
インスタンスの状態を保持したかったり、クラス間で共通のメソッド・プロパティにアクセスしたい場合に使われることが多いですね。
Singletonパターンのクラス図
- クラス変数としてsingletonを定義
- インスタンスを得るためのstasticメソッドで同じインスタンスを返す
具体例
生成したインスタンスが同一かどうか簡単な例で確かめてみましょう。
Mainクラス
以下のようにgetInstance()
によって得たインスタンスが同一のものか確かめてみます
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クラス
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
指定し、同期処理をさせる必要があります。
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();
}
}
- Singletonクラスがロードされた時点では、Singletonインスタンスは生成されない
- Singleton#getInstance()を最初に呼び出した時に、SingletonHolderクラスがロードされ、Singletonインスタンスが生成される
synchronizedを使用することなく、ギリギリまでSingletonインスタンスを生成しないような挙動になるようです。
iOS(Objective-C)の場合
@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のシングルトンの話
追記
- Javascriptの場合は、オブジェクト全てがSingletonとみなされる
- 参照: 【JSでデザインパターン】シングルトン編
- Singletonの別の方法として、Monostateパターンもある
- 参照: 実践デザパタ-その13:Monostateパターン
問題点
- Singletonクラスを明示的に開放するための機能がない、もしくは自作で適切なタイミングで解放する必要がある
- 詳細: シングルトンのベターな実装方法
- クラス間の依存関係や状態が一見してわからないので、コードから推測するのが困難
- ユニットテストのモック運用や状態設定が困難になる
サンプルコードについて
以下のレポジトリにソースコードをアップしてあります。
shoheiyokoyama/design-pattern_java
デザインパターン
- 生成に関するパターン
- Abstract factory
- Builder
- Factory Method
- Prototype
- Singleton
- 構造に関するパターン
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
- 振る舞いに関するパターン
- Chain of responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor