Java : Singleton パターン (図解/デザインパターン)
Singleton パターンとは、GoF によって定義されたデザインパターンの1つです。
そのクラスのインスタンスが1つしか生成されないことを保証します。
本記事では、Singleton パターンについてコード付きで解説していきます。
デザインパターン(GoF) 関連記事
- 生成に関するパターン
- 振る舞いに関するパターン
概要
Singleton パターンとは、
- あるクラスがあり
- そのクラスのインスタンスが最大で1つしか生成されない
ということを保証するための設計です。
Java においてインスタンスの生成とは、クラスに対して new 演算子を使うことをいいます。
class Sample {
}
// Sample クラスのインスタンスを生成
final var instance = new Sample();
さて、Singleton パターンはどのような場面で有用でしょうか?
先ほど引用した Wikipedia では、
- ロケールやルック・アンド・フィールなど、絶対にアプリケーション全体で統一しなければならない仕組みの実装に使用される
とあります。
他には、アプリケーションの基盤となりうるフレームワークなどにも使ってよいかもしれませんね。
【補足】
個人的には、Singleton パターンを積極的に使うことはおすすめしません。
Singleton パターンにはデメリットもあります。
使う前に、
- 本当にシングルであることが必要か?
ということはよく検討したほうがよいでしょう。
デメリットについては下記の記事でも解説していますので、合わせてご確認いただけると幸いです。
クラス図とコード例
上の図は、Singleton パターンの一般的なクラス図となります。
このクラス図に対して実装の仕方はいろいろありますが、Java で推奨されている実装例は次のようになります。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
// テスト用
public void hello() {
System.out.println("Hello, Singleton!");
}
}
hello メソッドはテスト用です。
実際に Singleton クラスを使ってみましょう。
final var instance = Singleton.getInstance();
instance.hello();
// 結果
// ↓
// Hello, Singleton!
Singleton クラスを使う側では、new 演算子ではなく、代わりに getInstance メソッドでインスタンスを取得します。
そして、テスト用の hello メソッドを呼び出しました。
getInstance で取得されるインスタンスは、
- どこから呼び出しても
- 何回呼び出しても
必ず 同一 のものとなります。
つまり、インスタンスは1つ というわけですね。
これが、Sigleton クラスを使う一連の流れになります。
それでは、もう少し細かく見ていきましょう。
外からのインスタンス生成を抑止
1つめのポイントは、コンストラクタの アクセス修飾子 を private にすることです。
public class Singleton {
private Singleton() {
^^^^^^^ <--- アクセス修飾子
}
}
これにより、Singleton クラスの外では、Singleton のインスタンス生成ができなくなります。
例えば Main クラスで Singleton クラスを new しようとするとコンパイルエラーになります。
public class Main {
public static void main(String[] args) {
// コンパイルエラー
final var instance = new Singleton();
}
}
ただ、外から Singleton クラスのインスタンスがまったく使えないのはダメですよね。
そこで、インスタンスを取得するための getInstance メソッドが必要となります。
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
...
}
}
外からは、new 演算子の代わりに getInstance を使ってインスタンスを取得します。
// インスタンスを取得
final var instance = Singleton.getInstance();
getInstance メソッドで返すインスタンスを毎回同じものに制限できれば、それは Singleton パターンとなります。
インスタンスを1つに制限
インスタンスを1つに制限するために、Java では static フィールドを使います。
static フィールドは、クラス変数とも呼ばれています。
public class Singleton {
private static final Singleton instance = new Singleton();
^^^^^^ ^^^^^^^^
...
}
static フィールドは、
- クラスのインスタンスがいくつ生成されても
- もしくはインスタンスが0個でも
実体は1つだけになります。
Singleton パターンではインスタンスは1つだけ生成します。
その1つのインスタンスを保持するのに、static フィールドは都合がよいわけですね。
マルチスレッド対応
実は、前述してきたコード例はマルチスレッド環境で安全に使えます。
それは、static フィールドの初期化時に Singleton クラスのインスタンスを生成しているためです。
public class Singleton {
private static final Singleton instance = new Singleton();
^^^^^^ ^^^^^^^^^^^^^^^
...
}
static フィールドの初期化処理は、そのクラスの初期化時(ロード時) に 一度だけ 実行されます。
Java 言語仕様にも明記されています。
複数のスレッドから同時に getInstance メソッドを呼び出したとしても、そのときにはクラスのロードは完了しています。
(ロードが完了していないと、そもそも getInstance メソッドを呼び出せないので…)
クラスのロードが完了しているということは、
private static final Singleton instance = new Singleton();
この処理も完了しているということです。
よって、複数のスレッドからこの Singleton クラスを使う場合でも、getInstance メソッドに synchronized キーワードは必要はありません。
public static synchronized Singleton getInstance() {
^^^^^^^^^^^^ <---- 必要なし!
return instance;
}
補足
- 正確には、Singleton クラスのインスタンス生成と getInstance メソッドはマルチスレッドに対して安全です。
しかし、テスト用の hello メソッドは安全ではない(同期されていない) のでご注意ください。
もしマルチスレッドをまったく考慮しなくてよいのであれば、次のような実装でも問題ありません。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
ただ、getInstancce のたびに、
if (instance == null) {
...
}
このチェックをするのは、ちょっと無駄な気もしますよね…
やはり、static フィールドの初期化処理でインスタンス生成してしまうのがおすすめです。
getInstance メソッドも余計な処理が入らずにシンプルになります。
まとめ
Singleton パターンは、
- あるクラスがあり
- そのクラスのインスタンスが最大で1つしか生成されない
ということを保証するための設計です。
ただし、デメリットもありますので、よく検討してから使うことをおすすめします。
関連記事
- 標準APIにならう命名規則
- コメントが少なくて済むコードを目指そう
- シングルトン・パターンの乱用はやめよう
- メソッドのパラメータ(引数)は使う側でチェックしよう
- 不変オブジェクト(イミュータブル) とは
- 依存性の注入(DI)をもっと気軽に
- 不要になったコードはコメントアウトで残さずに削除しよう
- 簡易的な Builder パターン
- 読み取り専用(const) のインタフェースを作る
- 図解/デザインパターン一覧 (GoF)
- Abstract Factory パターン
- Adapter パターン
- Bridge パターン
- Builder パターン
- Chain of Responsibility パターン
- Command パターン
- Composite パターン
- Decorator パターン
- Facade パターン
- Factory Method パターン
- Flyweight パターン
- Interpreter パターン
- Iterator パターン
- Mediator パターン
- Memento パターン
- Observer パターン
- Prototype パターン
- Proxy パターン
- State パターン
- Strategy パターン
- Template Method パターン
- Visitor パターン