제이온

[Effective Java] アイテム3. プライベートコンストラクタまたは列挙型でシングルトンであることを保証する

作成: 2024-04-27

作成: 2024-04-27 00:48

シングルトン

シングルトンの概念

シングルトンとは、インスタンスを1つしか生成できないクラスのことです。シングルトンの典型的な例としては、ステートレスなオブジェクトや、一意のシステムコンポーネントが挙げられます。しかし、シングルトンは、型をインターフェースとして定義し、その実装として定義するのでなければ、テストが難しいという問題があります。


シングルトンを作成する方法

**public staticメンバーがfinalフィールドである方式**



privateコンストラクタは、Elvisインスタンスを初期化するときに、ちょうど1回だけ呼び出され、システム全体で一意のインスタンスであることを保証します。ただし、AccessibleObject.setAccessible()を使用して、privateコンストラクタを呼び出すことができますが、このようなリフレクションによる改変は、2番目のオブジェクトが生成されるときに例外を投げることで防ぐことができます。


  • 利点
    • そのクラスがシングルトンであることがAPIに明示的に示されている。
    • 簡潔である。


**静的ファクトリメソッドをpublic staticで提供する方式**


リフレクションによる改変を除けば、この方法もシステム全体で一意のインスタンスであることを保証します。単にフィールドをprivateにし、オブジェクトの返却に静的ファクトリメソッドを使用しているだけです。


  • 利点
    • APIを変更せずに、シングルトンではなく変更できる。
      • 例えば、静的ファクトリメソッドが呼び出されるスレッドごとに異なるインスタンスを返すようにすることができます。
    • 必要に応じて、ジェネリックシングルトンファクトリメソッドに変更できます。
    • 静的ファクトリメソッドのメソッド参照をサプライヤとして使用できます。
      • 例えば、Elvis::getInstanceの代わりにSupplier<Elvis>として使用できます。


上記の利点を活用する必要がない場合は、最初の方法を使用する方が良いでしょう。


**列挙型を使用する方式**


最も望ましい方法は、列挙型を使用することです。上記の2つの方法と比べて、リフレクション攻撃にも安全で、コードもクリーンです。また、後述しますが、上記の2つの方法は、シリアライズする際に追加のコードを挿入する必要があるという欠点があります。

ただし、生成しようとするシングルトンがインターフェースを継承することは可能ですが、クラスを継承することはできないことに注意する必要があります。


シングルトンクラスをシリアライズする際の注意点

上記の方法のうち、最初の方法または2番目の方法で作成したシングルトンクラスをシリアライズするには、単にSerializableを実装するだけでなく、すべてのインスタンスフィールドをtransientに宣言し、readResolve()メソッドをオーバーライドして提供する必要があります。



出典

コメント0