シングルトンと静的クラスのどちらを使用するかを決定する際の設計上の考慮事項を挙げてください。これを行うと、2 つを対比することを余儀なくされるので、思いつく対比は、思考プロセスを示すのにも役立ちます。また、すべてのインタビュアーは実例を見るのが好きです。:)
22 に答える
- シングルトンはインターフェイスを実装し、他のクラスから継承できます。
- シングルトンは遅延ロードできます。実際に必要な場合のみ。初期化に高価なリソースの読み込みやデータベース接続が含まれる場合、これは非常に便利です。
- シングルトンは実際のオブジェクトを提供します。
- シングルトンはファクトリに拡張できます。舞台裏のオブジェクト管理は抽象的であるため、保守性が向上し、コードが改善されます。
「両方を避ける」のはどうですか?シングルトンと静的クラス:
- グローバル状態を導入する可能性があります
- 他の複数のクラスと密結合する
- 依存関係を隠す
- 分離したクラスの単体テストが困難になる可能性がある
代わりに、依存性注入とコントロール コンテナライブラリの反転を調べてください。IoC ライブラリのいくつかは、ライフタイム管理を処理します。
(いつものように、静的数学クラスや C# 拡張メソッドなどの例外があります。)
唯一の違いは構文だと思います: MySingleton.Current.Whatever() 対 MySingleton.Whatever()。David が述べたように、状態は最終的には「静的」です。
編集:埋葬旅団がdiggからやって来ました...とにかく、シングルトンが必要になるケースを考えました。静的クラスは、基本クラスから継承することも、インターフェイスを実装することもできません (少なくとも .Net ではできません)。したがって、この機能が必要な場合は、シングルトンを使用する必要があります。
この問題に関する私のお気に入りの議論の 1 つがここにあります(元のサイトはダウンしており、現在はInternet Archive Wayback Machineにリンクされています)。
シングルトンの柔軟性の利点を要約すると、次のようになります。
- シングルトンは簡単にファクトリに変換できます
- シングルトンは、異なるサブクラスを返すように簡単に変更できます
- これにより、アプリケーションの保守性が向上します
静的変数がたくさんある静的クラスはちょっとしたハックです。
/**
* Grotty static semaphore
**/
public static class Ugly {
private static int count;
public synchronized static void increment(){
count++;
}
public synchronized static void decrement(){
count--;
if( count<0 ) {
count=0;
}
}
public synchronized static boolean isClear(){
return count==0;
}
}
実際のインスタンスを持つシングルトンの方が優れています。
/**
* Grotty static semaphore
**/
public static class LessUgly {
private static LessUgly instance;
private int count;
private LessUgly(){
}
public static synchronized getInstance(){
if( instance==null){
instance = new LessUgly();
}
return instance;
}
public synchronized void increment(){
count++;
}
public synchronized void decrement(){
count--;
if( count<0 ) {
count=0;
}
}
public synchronized boolean isClear(){
return count==0;
}
}
状態はインスタンス内でのみです。
したがって、シングルトンを後で変更して、プーリングやスレッドローカルインスタンスなどを実行できます。また、メリットを得るために、すでに記述されているコードを変更する必要はありません。
public static class LessUgly {
private static Hashtable<String,LessUgly> session;
private static FIFO<LessUgly> freePool = new FIFO<LessUgly>();
private static final POOL_SIZE=5;
private int count;
private LessUgly(){
}
public static synchronized getInstance(){
if( session==null){
session = new Hashtable<String,LessUgly>(POOL_SIZE);
for( int i=0; i < POOL_SIZE; i++){
LessUgly instance = new LessUgly();
freePool.add( instance)
}
}
LessUgly instance = session.get( Session.getSessionID());
if( instance == null){
instance = freePool.read();
}
if( instance==null){
// TODO search sessions for expired ones. Return spares to the freePool.
//FIXME took too long to write example in blog editor.
}
return instance;
}
静的クラスで同様のことを行うことは可能ですが、間接ディスパッチでは呼び出しごとのオーバーヘッドが発生します。
インスタンスを取得して、引数として関数に渡すことができます。これにより、コードを「正しい」シングルトンに送ることができます。必要になるのは1つだけです...必要がなくなるまで。
大きな利点は、ステートフルシングルトンをスレッドセーフにすることができるのに対し、静的クラスは、シークレットシングルトンに変更しない限りできないことです。
シングルトンをサービスのようなものと考えてください。特定の機能セットを提供するオブジェクトです。例えば
ObjectFactory.getInstance().makeObject();
オブジェクト ファクトリは、特定のサービスを実行するオブジェクトです。
対照的に、静的メソッドでいっぱいのクラスは、実行したいアクションのコレクションであり、関連するグループ (クラス) に編成されています。例えば
StringUtils.reverseString("Hello");
StringUtils.concat("Hello", "World");
ここでの StringUtils の例は、どこにでも適用できる機能のコレクションです。シングルトン ファクトリ オブジェクトは、必要に応じて作成して渡すことができる明確な責任を持つ特定の種類のオブジェクトです。
静的クラスは実行時にインスタンス化されます。これには時間がかかる場合があります。シングルトンは、必要な場合にのみインスタンス化できます。
「静的クラス」が静的変数のみを持つクラスを意味する場合、それらは実際に状態を維持できます。私の理解では、唯一の違いは、このものへのアクセス方法です。例えば:
MySingleton().getInstance().doSomething();
対
MySingleton.doSomething();
MySingleton の内部構造は明らかに異なりますが、スレッド セーフの問題は別として、クライアント コードに関してはどちらも同じように動作します。
通常、シングルトン パターンは、複数のスレッドが同時にデータにアクセスできるインスタンスに依存しないデータまたは静的データを処理するために使用されます。1 つの例として州コードがあります。
シングルトンは絶対に使用しないでください (可変状態のないクラスをシングルトンと見なさない限り)。「静的クラス」には、おそらくスレッドセーフなキャッシュなどを除いて、変更可能な状態があってはなりません。
シングルトンのほとんどすべての例は、そうしない方法を示しています。
シングルトンが静的クラスよりも理にかなっているのは、コストのかかるリソース(データベース接続など)のプールを構築する必要がある場合だと思います。誰もプールを使用しない場合は、プールを作成することに興味はありません(静的クラスは、クラスがロードされたときにコストのかかる作業を行うことを意味します)。
シングルトンには、コンストラクタとデストラクタがある場合があります。言語によっては、シングルトンが初めて使用されたときにコンストラクターが自動的に呼び出される場合と、シングルトンがまったく使用されていない場合は呼び出されない場合があります。静的クラスには、そのような自動初期化はありません。
シングルトン オブジェクトへの参照が取得されると、他のオブジェクトと同様に使用できます。シングルトンへの参照が以前に保存されている場合、クライアントコードはシングルトンを使用していることを知る必要さえない場合があります。
Foo foo = Foo.getInstance();
doSomeWork(foo); // doSomeWork wont even know Foo is a singleton
これにより、Singleton パターンを捨てて IoC などの実際のパターンを使用することを選択した場合に、作業が明らかに簡単になります。
ルックアップ テーブルなど、できればコンパイル時に計算するものを実行時に計算する必要がある場合は、シングルトン パターンを使用します。
データの効率的なキャッシュを強制したい場合は、シングルトンも良い考えです。たとえば、xml ドキュメントで定義を検索するクラスがあります。ドキュメントの解析には時間がかかることがあるため、定義のキャッシュを設定します (outOfmemeoryErrors を回避するために SoftReferences を使用します)。必要な定義がキャッシュにない場合は、高価な xml 解析を行います。それ以外の場合は、キャッシュからコピーを返します。複数のキャッシュがあるということは、同じ定義を複数回ロードしなければならない可能性があることを意味するため、静的キャッシュが必要です。通常の (非静的) データ メンバーのみを使用してクラスを記述できるように、このクラスをシングルトンとして実装することにしました。これにより、何らかの理由 (シリアル化、単体テストなど) で必要になった場合でも、クラスのインスタンス化を作成できます。
静的クラスを引数として渡すことはできません。シングルトンのインスタンスが可能です。他の回答で述べたように、静的クラスのスレッドの問題に注意してください。
rp
すでに述べたように、Singleton はサービスのようなものです。プロはその柔軟性です。静的です。Singleton を実装するには、いくつかの静的パーツが必要です。
シングルトンには、実際のオブジェクトのインスタンス化を処理するコードがあり、競合の問題が発生した場合に非常に役立ちます。静的ソリューションでは、複数のコードの場所で競合の問題に対処する必要がある場合があります。
ただし、シングルトンがいくつかの静的変数で構築できるのと同じように、「goto」と比較できる場合があります。他の構造を構築するのに非常に役立ちますが、実際にはその使用方法を知る必要があり、「使いすぎ」てはなりません。したがって、一般的な推奨事項は、Singleton に固執し、必要に応じて static を使用することです。
他の投稿も確認してください:シングルトン実装よりも静的クラスを選択する理由は?
シングルトンが処分できるものである場合は、後でクリーンアップするために、常に必要としない限られたリソース(つまり、1つだけ)であり、何らかのメモリまたは割り当て時のリソース コスト。
静的状態フィールドを含む静的クラスとは対照的に、シングルトンを使用すると、クリーンアップ コードがより自然に見えます。
ただし、コードはどちらの方法でも同じように見えるため、質問するより具体的な理由がある場合は、詳しく説明する必要があります.
この 2 つは非常に似ていますが、真のシングルトンはそれ自体をインスタンス化 (1 回許可) してから提供する必要があることに注意してください。のインスタンスを返す PHP データベース クラスは、mysqli
実際にはシングルトンではありません (一部の人はそう呼んでいます)。静的メンバーとしてインスタンスを持つクラスのインスタンスではなく、別のクラスのインスタンスを返すためです。
したがって、コード内で のインスタンスを 1 つだけ許可する予定の新しいクラスを作成している場合は、それを Singleton として作成することもできます。単純なジェーン クラスを作成し、単一インスタンス化の要件を容易にするために追加するものと考えてください。変更できない他の誰かのクラス ( などmysqli
) を使用している場合は、静的クラスを使用する必要があります (その定義の前にキーワードを付けなくても)。
シングルトンはより柔軟です。これは、Instance メソッドが何らかのコンテキストに基づいてシングルトンの型のさまざまな具象サブクラスを返すようにする場合に役立ちます。
単一のクラスが状態を必要とする場合。シングルトンはグローバルな状態を維持しますが、静的クラスは維持しません。
たとえば、レジストリ クラスの周りにヘルパーを作成します。変更可能なハイブ (HKey Current User と HKEY Local Machine) がある場合は、次のようにします。
RegistryEditor editor = RegistryEditor.GetInstance();
editor.Hive = LocalMachine
これで、そのシングルトンへの以降の呼び出しは、ローカル マシン ハイブで動作します。それ以外の場合は、静的クラスを使用して、ローカル マシンのハイブを毎回指定するか、ReadSubkeyFromLocalMachine
.