シングルトン(Singleton)とは
シングルトン(Singleton)とは、クラスのインスタンスを1つしか作成しないデザインパターンのことを指します。
シングルトンのクラスは、最初の1度のみ作成したインスタンスを全体で使い回すため、アプリケーション全体で共有する情報などを管理するのに向いています。
通常 Java では、インスタンスを生成する時に new キーワードを使用しますが、シングルトンのクラスにおいては、 コンストラクタを private にして new キーワードでインスタンスが生成できないように抑制し、その代わりにインスタンスを取得するためのメソッド static メンバーに用意し、インスタンスを取得するのが一般的な実装パターンです。
では実際に、Java でシングルトンパターンを実装する方法を見ていきましょう。
シングルトン・クラスを書いてみよう
Java でシングルトン・クラスを実装する時のポイントは次のとおりです。
- コンストラクタを private スコープにする
- 雄一のインスタンスを取得する static な getter メソッドを用意する
実際に、 Java でシングルトンを実装したクラスのサンプルコードを作ってみましょう。
/**
* シングルトン・クラスのサンプル
*/
public class SingletonSample {
// このクラスの雄一のインスタンスを保持する
private static SingletonSample instance = new SingletonSample();
/**
* コンストラクタ
* 外部から new でインスタンスが生成できないように private スコープにする
*/
private SingletonSample() {
// ここに雄一のインスタンスを作成する時の初期化処理を書く
// 例えば、設定ファイルから値を読み取る処理など
}
/**
* 雄一のインスタンスを取得するための getter メソッド
* @return このクラスの雄一のインスタンス
*/
public static SingletonSample getInstance() {
return instance;
}
// シングルトンクラス内のメソッド
// 初期化時に読み込んだ設定値などを返したりする
public String getSetting(String key) {
return "設定値などを返す";
}
}
シングルトンクラスを使用する
上で作成したシングルトンクラスを使用する場合は、次のようなコードを書きます。
public class Main {
public static void main(String[] args) {
// getInstance() メソッドを通じて、SingletonSampleクラスの雄一のインスタンスを取得
SingletonSample instance = SingletonSample.getInstance();
System.out.println(instance.getSetting("key"));
}
}
シングルトンパターンの利用例
シングルトンは1度作成したインスタンスを、全体の処理で使いまわす実装パターンですが、具体的には、どういった利用シーンがあるのか確認してみましょう。
設定ファイルの管理
設定ファイルなどのリソース管理をシングルトンクラスで実装するのは、シングルトンパターンの王道と言ってよいでしょう。
シングルトンクラスの初期化時(雄一のインスタンス生成時)に設定ファイルから値を読み取り、内部の変数などに格納し、処理を行うクラスからはシングルトンクラスに格納した変数の内容から設定値を読み取ることで、毎回設定ファイルへアクセスすることなく、値を読み取ることを可能にします。
ロギング(ログクラス)
アプリケーションのデバッグ情報などのロギングについても、シングルトンパターンで実装されるケースが多いです。
ログは、基本的には1つのファイルに対し出力し、複数のクラスやスレッドから出力が行われます。
特にマルチスレッド環境下でログを出力する場合は、複数のスレッドから同時に1つのログファイルへ書き込みが行われると競合が発生するため、ログファイルへ書き込む内容をキューやキャッシュに記録しておき、それらの情報をマージしてログファイルに書き込む必要があり、このような時に、1つのインスタンに情報が記録できるシングルトンパターンの実装が適しています。
シングルトン使用時の注意点
シングルトンパターンは、設計やその利用方法がシンプルであることから、ついつい使ってしまいがちになりますが、いくつかの点で注意すべきことがあります。
本当に1つのインスタンスでよいのか?
シングルトンでクラスを実装する前に、本当にそのクラスのインスタンスが1つでよいのか検討する必要があります。また、その時点のみならず、将来にわたって本当に1つのインスタンスだけでよいか考える必要があります。
例えば、ロギングを行うクラスをシングルトンを利用するケースでも、アプリケーションの設計変更や使用しているライブラリの都合上、複数のロギングクラスのインスタンスに分けて出力する必要になった場合、一度完成したソースコードを大幅に変更することになります。
このように将来的にシングルトンが保てなくなる予想がついている場合には、依存性注入(DI)などの、汎用性が高い別のアプローチでクラスのインスタンを渡す方法を予め検討しておきましょう。
スレッドセーフであり、処理をブロックしないこと
シングルトンは、その名の通りクラスのインスタンスが1つしかないため、マルチスレッド環境下では、複数のスレッドから同時にシングルトンクラスにアクセスが入る可能性があり、当然ながらスレッドセーフにする設計が必要です。
シングルトンのメソッド内で、非スレッドセーフなAPIを使用している場合は、synchronized 修飾子を使用して、スレッド間で処理を排他的に実行させることによりスレッドセーフにする事自体は容易ですが、これにより処理のブロッキングが発生し、アプリケーション全体の処理が遅延する恐れがあるため、安易にシングルトンのクラスで synchronized 修飾子を使用することは注意が必要です。
状態は持たない
シングルトンのクラスに状態を持つと、それはグローバル変数に状態を持つことと同義となります。グローバル変数はクラス間の結合度を上げてしまい、テストの自動化(ユニットテスト)の妨げになるか、テストの自動化自体を不可能にしてしまいます。
また、状態を変更する時に、マルチスレッド間で処理を同期するために、synchronized 修飾子を使う事になり、結果的に前述したアプリケーションの処理遅延に繋がるため、可能な限り状態をシングルトンのクラスを持つことは避ける必要があります。
まとめ
Java でシングルトンのクラスを実装する方法と、シングルトンのクラスを設計する時に必要な注意点を解説してきました。
シングルトンのクラスを作る前に、依存性注入(DI)により代替できないか確認しながらクラスの設計を行なっていきましょう。
【関連記事】
▶多用は注意!Javaでグローバル変数を作る方法を解説
前述のように、シングルトンはリソースの管理に向いている実装パターンと言えます。ファイルなどの実体が1つかないリソースに対し、複数のインスタンスからアクセスを行うと不整合が生じることがあるため、こういったケースにおいてはシングルトンパターンが適しています。