デザインパターンについて:シングルトンはいつ使用すべきですか?


448

栄光のグローバル変数-栄光のグローバルクラスになります。オブジェクト指向の設計を破ると言う人もいます。

シングルトンを使用することが理にかなっている古き良きロガー以外のシナリオを教えてください。


4
アーランを学ぶので、私はそのアプローチ、すなわち不変性とメッセージパッシングを好みます。
せとり09年

209
この質問について建設的ではないものは何ですか?以下に建設的な回答を示します。
mk12 2013年

3
依存関係注入フレームワークは、オブジェクトを提供する非常に複雑なシングルトンです…。
Ian Ringrose 2014

1
シングルトンは他のオブジェクトのインスタンス間のマネージャーオブジェクトとして使用できるため、シングルトンのインスタンスを介して通信する必要があるシングルトンのインスタンスは1つだけです。
Levent Divilioglu

余計な質問があります。シングルトンの実装は、「静的」クラス(「factory」/「init」メソッドを使用)を使用して実装することもできます。実際にクラスのインスタンスを作成する必要はありません(静的クラスは、一種のシングルトン実装ですが...)-静的クラスの代わりに実際のシングルトン(単一であることを確認する単一クラスインスタンス)を使用する必要があるのはなぜですか?私が考えることができる唯一の理由は、おそらく「セマンティクス」のためですが、その意味でさえ、シングルトンのユースケースは、定義上、実際には「クラス->インスタンス」関係を必要としません...それで...なぜですか?
Yuval A.

回答:


358

私が真実を追求した結果、シングルトンを使用する "受け入れられる"理由は実際にはほとんどないことがわかりました。

インターネットで何度も何度も発生する傾向がある1つの理由は、「ロギング」クラス(あなたが言及した)のそれです。この場合、通常、プロジェクトのすべてのクラスでログクラスを何度も繰り返し使用する必要があるため、クラスの単一インスタンスの代わりにシングルトンを使用できます。すべてのクラスがこのロギングクラスを使用すると、依存関係の注入が面倒になります。

ログはコードの実行に影響を与えないため、「受け入れ可能な」シングルトンの具体的な例です。ロギングを無効にすると、コードの実行は変わりません。同じことを有効にします。ミスコはそれをシングルトンの根本原因として次のように述べています記載しています。「ここでの情報は一方向に流れる:アプリケーションからロガーへ。ロガーはグローバルな状態ですが、ロガーからアプリケーションに情報が流れないため、ロガーは受け入れられます。」

他にも正当な理由があると思います。Alex Millerは、「Patterns I Hate」で、サービスロケータとクライアント側のUIについても「許容できる」選択肢であると語っています。

シングルトンで詳細を読む愛していますが、あなたは私を失望させています。


3
@ArneMertz私はこれだと思います。
2013

1
なぜグローバルオブジェクトを使用できないのですか?なぜそれがシングルトンでなければならないのですか?
2015

1
ロギングユーティリティの静的メソッドと思いますか?
スカイネット

1
シングルトンは、リソースを管理する必要がある場合に最適です。たとえば、HTTP接続。1つのクライアントに対して100万のHTTPクライアントを確立する必要はありません。そのため、接続プールされたhttpクライアントを備えたシングルトンは、はるかに高速でリソースに優しいものになります。
Cogman

3
これは古い質問であり、この回答の情報は素晴らしいものです。ただし、OPで「シングルトンを使用するのが理にかなっている古き良きロガー以外のシナリオを教えてください」と明確に指定されている場合、これが受け入れられる回答である理由を理解できません。
フランシスコC.

124

シングルトン候補は、次の3つの要件を満たしている必要があります。

  • 共有リソースへの同時アクセスを制御します。
  • リソースへのアクセスは、システムの複数の異なる部分から要求されます。
  • オブジェクトは1つだけです。

提案するシングルトンにこれらの要件が1つまたは2つしかない場合、ほとんどの場合、再設計が正しいオプションです。

たとえば、プリンタスプーラが複数の場所([印刷]メニュー)から呼び出されることはほとんどないため、ミューテックスを使用して同時アクセスの問題を解決できます。

シンプルなロガーは、おそらく有効なシングルトンの最も明白な例ですが、これはより複雑なロギングスキームで変わる可能性があります。


3
私はポイント2に同意しません。ポイント3は実際には理由ではありません(それができるというわけではないからといって)、1は良いポイントですが、それでもまだ使用できません。共有リソースがディスクドライブまたはデータベースキャッシュであるとしましょう。別のドライブを追加したり、別のものに焦点を当てたdbキャッシュを作成したりできます(1つのスレッド用の特殊なテーブルのキャッシュ、他のスレッドはより汎用的なものなど)。

15
「候補者」という言葉を逃したと思います。シングルトン候補は、3つの要件を満たす必要があります。何かが要件を満たしているからといって、それがシングルトンである必要があるという意味ではありません。他にもデザイン要素があるかもしれません:)
metao '07 / 07/04

45

起動時にのみ読み取る必要がある構成ファイルを読み取り、それらをシングルトンにカプセル化します。


8
Properties.Settings.Default.NET と同様。
Nick Bedford

9
@Paul、「シングルトンなしのキャンプ」は、構成オブジェクトをグローバルにアクセス可能にする(別名シングルトン)のではなく、それを必要とする関数に渡すだけでよいと述べています。
Pacerier 2014年

2
同意しない。構成をデータベースに移動すると、すべてが台無しになります。構成へのパスがそのシングルトンの外部に依存している場合、これらも静的である必要があります。
rr-

3
@PaulCroarkinこれを拡張して、これがどのように有益であるかを説明できますか?
AlexG 2017

1
@ rr-構成がデータベースに移動した場合でも、それを必要とする関数に渡される構成オブジェクトにカプセル化できます。(私は「シングルトンなし」のキャンプにいません)。
シェパードは

36

共有リソースを管理する必要がある場合は、シングルトンを使用します。たとえば、プリンタースプーラー。同じリソースに対する要求の競合を回避するために、アプリケーションにはスプーラーの単一インスタンスのみが必要です。

または、データベース接続またはファイルマネージャなど。


30
私はこのプリンタースプーラーの例を聞いたことがありますが、それは一種の足りないと思います。スプーラーを複数持つことはできないと誰が言ったのですか?とにかく、プリンタースプーラーは一体何ですか?競合しない、または異なるドライバーを使用できない異なる種類のプリンターがある場合はどうなりますか?
1800情報

6
その例にすぎません...誰もが例として使用している状況では、例を役に立たないようにする代替設計を見つけることができます。スプーラが複数のコンポーネントで共有される単一のリソースを管理しているとしましょう。できます。
Vincent Ramdhanie、2008年

2
ギャングオブフォーの典型的な例です。実際に試してみたユースケースでの回答がより役立つと思います。シングルトンが最良のソリューションであると実際に感じた状況を意味します。
Andrei Vajna II

私の意見では、共有リソースは非常に広い例です。「スプーラ」の誤動作する実装を注入できないときに、誤動作するスプーラに直面しても、印刷スプーラを使用するオブジェクトが正しく機能していることをどのようにテストしますか?短くて有益ではありませんが、私の本では、受け入れられた答えの方がはるかに安全なアプローチです
Rune FS '20

一体何がプリンタースプーラーですか?
RayLoveless、2018年

23

グローバル状態(ユーザー言語、ヘルプファイルパス、アプリケーションパス)を格納する読み取り専用のシングルトンは妥当です。シングルトンを使用してビジネスロジックを制御する場合は注意してください-シングルはほとんどの場合複数になります


4
システムを使用できるのは1人のユーザーだけであるという前提で、ユーザー言語はシングルトンのみにすることができます。
SamuelÅslund14年

…そして、その1人のユーザーは1つの言語しか話せません。
スペクトル

17

データベースへの接続(または接続のプール)の管理。

また、外部構成ファイルの情報を取得して保存するためにも使用します。


2
データベース接続ジェネレータはファクトリの例ではないでしょうか?
Ken

3
@ケンは、ほとんどすべてのケースでそのファクトリをシングルトンにしたいと思うでしょう。
Chris Marisic、2011年

2
@Federico、「シングルトンなしのキャンプ」では、これらのデータベース接続は、グローバルにアクセスできるようにする(シングルトン)代わりに、それらを必要とする関数に渡すだけでよいと述べています。
Pacerier 2014年

3
これにはシングルトンは本当に必要ありません。注射可能です。
Nestor Ledon 2017

11

シングルトンを使用する方法の1つは、リソースへのアクセスを制御する単一の「ブローカー」が必要なインスタンスをカバーすることです。シングルトンは、たとえば、排他的にのみ書き込み可能なファイルへのアクセスを仲介するため、ロガーに適しています。ロギングなどの場合、ログファイルのようなものへの書き込みを抽象化する方法を提供します。キャッシュメカニズムをシングルトンなどにラップすることができます

また、多数のウィンドウ/スレッド/その他を備えたアプリケーションがあるが、単一の通信ポイントを必要とする状況を考えてください。以前は、アプリケーションを起動するジョブを制御するために1つ使用しました。シングルトンは、ジョブをシリアル化し、興味のあるプログラムの他の部分にそのステータスを表示する責任がありました。この種のシナリオでは、シングルトンを、アプリケーション内で実行されている「サーバー」クラスのようなものとして見ることができます... HTH


3
ロガーはほとんどの場合シングルトンであるため、ロギングオブジェクトを渡す必要はありません。ログストリームを適切に実装すると、シングルトンかどうかにかかわらず、同時書き込みが不可能になります。
metao

10

アプリケーション全体で共有されるリソースへのアクセスを管理する場合はシングルトンを使用する必要があり、同じクラスの複数のインスタンスが存在する可能性があると破壊的です。共有リソースへのアクセスがスレッドセーフであることを確認することは、この種のパターンが重要になる可能性のある非常に良い例の1つです。

シングルトンを使用するときは、依存関係を誤って隠していないことを確認してください。理想的には、アプリケーションの初期化コード(C#実行可能ファイルの場合はstatic void Main()、Java実行可能ファイルの場合はstatic void main())の実行中にシングルトン(アプリケーションのほとんどの静的変数と同様)を設定してから、それを必要とするインスタンス化された他のすべてのクラス。これは、テスト容易性の維持に役立ちます。


8

シングルトンの使用は、データベースの多対1の関係と同じように考えることができると思います。オブジェクトの単一のインスタンスを処理する必要があるコードの多くの異なる部分がある場合は、シングルトンを使用するのが合理的です。


6

シングルトンの実用的な例は、Test :: Builderにあります。すべての最新のPerlテストモジュールをサポートするクラスにあります。Test :: Builderシングルトンは、テストプロセスの状態と履歴(履歴テスト結果、実行されたテストの数をカウント)、およびテスト出力の送信先などを保存および仲介します。これらはすべて、異なる作成者が作成した複数のテストモジュールを調整して、単一のテストスクリプトで連携させるために必要です。

Test :: Builderのシングルトンの歴史は教育的なものです。呼び出してもnew()常に同じオブジェクトが得られます。まず、すべてのデータは、オブジェクト自体に何もないクラス変数として格納されました。これは、Test :: Builderをそれ自体でテストするまで機能しました。次に、2つのTest :: Builderオブジェクトが必要でした。1つはダミーとして設定し、その動作と出力をキャプチャしてテストするためのもので、もう1つは実際のテストオブジェクトにするためのものです。その時点で、Test :: Builderは実際のオブジェクトにリファクタリングされました。シングルトンオブジェクトはクラスデータとして格納され、new()常にそれを返します。 create()新しいオブジェクトを作成し、テストを有効にするために追加されました。

現在、ユーザーはTest :: Builderのいくつかの動作を自分のモジュールで変更したいが、他はそのままにしておきたいが、テスト履歴はすべてのテストモジュールで共通である。現在起こっているのは、モノリシックなTest :: Builderオブジェクトが小さな部分(履歴、出力、フォーマット...)に分解され、それらを一緒に収集するTest :: Builderインスタンスです。Test :: Builderがシングルトンである必要はなくなりました。歴史のように、そのコンポーネントはそうすることができます。これにより、シングルトンの柔軟性のない必要性が1レベル下がります。これにより、ユーザーはピースを組み合わせてより柔軟に使用できます。小さなシングルトンオブジェクトは、データを格納するだけでよく、それらを含むオブジェクトがデータの使用方法を決定します。また、Test :: Builderの履歴と出力シングルトンを使用して、Test :: Builder以外のクラスを一緒に再生することもできます。

データの整合性と動作の柔軟性の間にプッシュアンドプルがあるように見えます。これは、データの整合性を保証するために、動作が最小限の共有データのみにシングルトンを配置することで軽減できます。


5

データベースまたはファイルから構成プロパティオブジェクトをロードする場合、それをシングルトンとして持つと役立ちます。サーバーの実行中に変化しない静的データを再読み取りし続ける理由はありません。


2
なぜデータを一度ロードして、必要に応じて構成オブジェクトを渡さないのですか?
ラグウィーズル2016

通りがかりとは何ですか?私は私が必要とするすべてのオブジェクトの周りを渡すために持っていた場合、私は20個の引数...とコンストラクタだろう
Enerccio

@Enerccioカプセル化されていない20の異なるオブジェクトに依存するオブジェクトがある場合、すでに大きな設計上の問題があります。
スペクトル

@spectrasしますか?GUIダイアログを実装する場合は、リポジトリ、ローカリゼーション、セッションデータ、アプリケーションデータ、ウィジェットの親、クライアントデータ、権限マネージャーなどが必要になります。もちろん、いくつかを集計できますが、なぜですか?個人的には、スプリングとアスペクトを使用して、これらすべての依存関係をウィジェットクラスに自動配線するだけで、すべてが分離されます。
Enerccio

それだけの状態がある場合は、ファサードの実装を検討して、特定のコンテキストに関連する側面のビューを提供することができます。どうして?それは、シングルトンまたは29引数のコンストラクターのアンチパターンを使用せずにすっきりとした設計を可能にするためです。実際、あなたのGUIダイアログがそれらすべてにアクセスするという事実は、「単一の責任原則の違反」を叫びます。
12:03のスペクトル

3

みんなが言ったように、共有リソース-特に同時アクセスを処理できないもの。

私が見た特定の例の1つは、Lucene Search Index Writerです。


1
それでも、IndexWriterはシングルトンではありません...
Mark

3

(GoFブックに示されている方法で)状態パターンを実装するときにシングルトンを使用できます。これは、具象Stateクラスには独自の状態がなく、コンテキストクラスの観点からアクションを実行するためです。

Abstract Factoryをシングルトンにすることもできます。


これは、私が現在プロジェクトで扱っているケースです。状態パターンを使用して、反復的な条件付きコードをコンテキストのメソッドから削除しました。状態には、独自のインスタンス変数はありません。しかし、私はそれらをシングルトンにするべきかどうかについてはフェンスのそばにいます。状態が切り替わるたびに、新しいインスタンスがインスタンス化されます。インスタンスが他と異なる可能性がないため、これは無駄に見えます(インスタンス変数がないため)。なぜそれを使用すべきでないのかを理解しようとしています。
kiwicomb123 2017年

1
@ kiwicomb123 setState()状態の作成ポリシーを決定する責任を自分に持たせるようにしてください。プログラミング言語がテンプレートまたはジェネリックをサポートしている場合に役立ちます。シングルトンの代わりに、モノステートパターンを使用することもできます。この場合、状態オブジェクトをインスタンス化すると、同じグローバル/静的状態オブジェクトが再利用されます。ユーザーはインスタンス化された状態がモノステートであることを意識する必要がないため、状態を変更するための構文は変更されないままである可​​能性があります。
Emile Cormier 2017年

わかりましたので、私の状態では、すべてのメソッドを静的にすることができます。そのため、新しいインスタンスが作成されるたびに、同じオーバーヘッドが発生しませんか?少し混乱していますが、モノステートパターンについて説明する必要があります。
kiwicomb123 2017年

@ kiwicomb123いいえ、Monostateはすべてのメンバーを静的にすることではありません。よく読んでから、SOで関連する質問と回答を確認してください。
Emile Cormier

これはもっと投票すべきだと思います。抽象ファクトリは十分に一般的であり、ファクトリはステートレスであり、ステートレスであるため安定しており、オーバーライドされない静的メソッド(Javaの場合)で実装できないため、シングルトンの使用は問題ありません。
DPM

3

共有リソース。特にPHPでは、データベースクラス、テンプレートクラス、およびグローバル変数デポクラスです。すべては、コード全体で使用されているすべてのモジュール/クラスで共有する必要があります。

それは本当のオブジェクトの使用法です->テンプレートクラスは構築されているページテンプレートを含み、ページ出力に追加しているモジュールによって整形、追加、変更されます。これが発生する可能性があるため、単一のインスタンスとして保持する必要があり、データベースについても同様です。共有データベースシングルトンを使用すると、すべてのモジュールのクラスがクエリにアクセスして、再実行しなくてもクエリを取得できます。

グローバル変数デポシングルトンは、グローバルで信頼性が高く、簡単に使用できる変数デポを提供します。コードを大幅に整理します。次のように、配列内のすべての構成値をシングルトンで想像してください。

$gb->config['hostname']

またはすべての言語の値を次のような配列で持つ:

$gb->lang['ENTER_USER']

ページのコードの実行が終了すると、たとえば、成熟したものになります。

$template

シングルトン、$gb置き換えるためのlang配列を持つシングルトン、およびすべての出力がロードされて準備ができています。それらを、成熟したテンプレートオブジェクトのページ値に現在存在するキーに置き換え、それをユーザーに提供するだけです。

これの大きな利点は、何でも好きな後処理ができることです。すべての言語値をgoogle翻訳または別の翻訳サービスにパイプして、それらを取り戻し、それらを翻訳済みなどの場所に置き換えることができます。または、必要に応じて、ページ構造またはコンテンツ文字列を置き換えることができます。


21
解答を複数の段落に分割し、読みやすくするためにコードセグメントをブロックすることができます。
Justin

1

特定のインフラストラクチャの問題をシングルトンまたはグローバル変数として構成することは、非常に実用的です。これの私のお気に入りの例は、フレームワークへの接続ポイントとして機能するシングルトンを利用する依存性注入フレームワークです。

この場合、インフラストラクチャに依存して、ライブラリの使用を簡素化し、不要な複雑さを回避します。


0

プラグ可能なモジュールを扱うときに、コマンドラインパラメーターをカプセル化するオブジェクトに使用します。メインプログラムは、ロードされるモジュールのコマンドラインパラメーターが何であるかを認識していません(どのモジュールがロードされているかさえ常に認識しているわけではありません)。たとえば、メイン自体がAをロードしますが、それ自体はパラメーターを必要としません(そのため、追加のポインター/参照/何が必要なのか、よくわかりません-汚染のように見えます)、モジュールX、Y、Zをロードします。これらのうち、XとZはパラメーターを必要とする(または受け入れる)ため、コマンドラインシングルトンにコールバックしてどのパラメーターを受け入れるかを伝え、実行時にユーザーがコールバックしてユーザーが実際に何か指定したかどうかを確認しますそのうちの。

多くの点で、CGIパラメータを処理するためのシングルトンは、クエリごとに1つのプロセスのみを使用している場合と同様に機能します(他のmod_ *メソッドはこれを行わないため、問題があります-したがって、 t mod_perlまたはその他の世界に移植する場合に備えて、mod_cgiの世界でシングルトンを使用します。


-1

おそらくコードの例です。

ここで、ConcreteRegistryはポーカーゲームのシングルトンであり、パッケージツリー全体の動作がゲームのいくつかのコアインターフェース(モデル、ビュー、コントローラー、環境などのファサード)にアクセスできるようにします。

http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html

エド。


1
リンクは壊れていますが、アプリケーション全体からアクセスされるシングルトンでビュー情報を登録している場合は、MVCのポイントがありません。ビューは、モデルを使用するコントローラーによって更新され、コントローラーと通信します。ここで聞こえるように、これはおそらくシングルトンの誤用であり、リファクタリングが必要です。
drharris

-9

1-最初の回答に対するコメント:

静的なLoggerクラスに同意しません。これは実装には実用的ですが、単体テスト用に置き換えることはできません。静的クラスをテストdoubleで置き換えることはできません。単体テストを行わない場合、ここでは問題は発生しません。

2-手作業でシングルトンを作成しないようにします。コンストラクターを使用して単純なオブジェクトを作成するだけで、コラボレーターをオブジェクトに挿入できます。シングルトンが必要な場合は、依存関係無効化フレームワーク(Spring.NET、Unity for .NET、Spring for Java)などを使用します。


11
回答の下部にあるリンクをクリックして、回答に直接コメントする必要があります。そのように読む方がはるかに簡単です。また、あなたが一番上で見た答えはおそらく最初ではありません。回答は常に並べ替えられます。
ロス、

なぜユニットテストのロギングが必要なのですか?
Enerccio

「静的Loggerクラス」と静的Logger インスタンスの間には大きな違いがあります。シングルトンパターンは「クラスを静的にする」ではなく、オブジェクトのインスタンスへのアクセスを静的にすることを示しています。したがって、たとえば、ILogger logger = Logger.SingleInstance();このメソッドは静的であり、ILoggerの静的に格納されたインスタンスを返します。「依存関係注入フレームワーク」の例を使用しました。ほとんどすべてのDIコンテナーはシングルトンです。それらの構成は静的に定義され、最終的に単一のサービスプロバイダーインターフェイスから/に格納されて格納されます。
Jon Davis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.