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

StackOverflow https://stackoverflow.com/questions/228164

  •  03-07-2019
  •  | 
  •  

質問

グローバライズされたグローバル変数-栄光のグローバルクラスになります。一部の人は、オブジェクト指向の設計を破ると言います。

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

役に立ちましたか?

解決

真実を探求した結果、実際に「許容できる」ものはほとんどないことがわかりました。シングルトンを使用する理由。

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

ロギングは、「許容範囲」の具体例です。シングルトンはコードの実行に影響しないためです。ロギングを無効にします。コードの実行は変わりません。同じように有効にします。 Miskoは、シングルトンの根本原因に次のように記述していますa>、"ここでの情報は、アプリケーションからロガーに流れる1つの方法です。ロガーはグローバル状態ですが、ロガーからアプリケーションに情報は流れないため、ロガーは受け入れられます。"

他の正当な理由もあると確信しています。 Alex Miller、" 嫌いなパターン」、サービスロケーターやクライアント側のUIの話も「受け入れられる」可能性があります。選択肢。

Singletonで詳細を読む愛していますが、あなたは私を落ち込ませています。

他のヒント

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

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

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

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

単純なロガーは、おそらく有効なシングルトンの最も明らかな例ですが、これはより複雑なロギングスキームで変更できます。

起動時にのみ読み込まれる設定ファイルを読み込み、シングルトンにカプセル化します。

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

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

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

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

これを使用して、外部構成ファイルの情報を取得および保存します。

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

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

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

シングルトンを使用する場合、依存関係を誤って隠さないように注意する必要があります。理想的には、アプリケーションの初期化コードの実行中に(アプリケーションのほとんどの静的変数のように)シングルトンを設定し(C#実行可能ファイルのstatic void Main()、java実行可能ファイルのstatic void main())に渡されますインスタンス化され、それを必要とする他のすべてのクラス。これにより、テスト容易性を維持できます。

シングルトンの実用的な例は、 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がシングルトンである必要がなくなりました。そのコンポーネントは、履歴のようになります。これにより、シングルトンという柔軟性のない必要性がレベルを下げます。これにより、ユーザーはピースを組み合わせてより柔軟に使用できます。より小さいシングルトンオブジェクトは、データを格納するだけで、含まれるオブジェクトがその使用方法を決定できるようになりました。 Test :: Builderの履歴と出力シングルトンを使用して、Test :: Builder以外のクラスも一緒に再生できます。

データの整合性と振る舞いの柔軟性の間にプッシュとプルがあるように見えますが、データの整合性を確保するために、可能な限り少ない振る舞いで共有データだけにシングルトンを配置することで軽減できます。

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

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

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

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

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

これは真のオブジェクトの使用法です->テンプレートクラスには、作成中のページテンプレートが含まれており、ページ出力に追加するモジュールによって整形、追加、変更されます。これは単一のインスタンスとして保持する必要があるため、これはデータベースにも当てはまります。共有データベースシングルトンを使用すると、すべてのモジュールのクラスがクエリにアクセスし、再実行せずに取得できます。

グローバル変数デポシングルトンは、グローバルで信頼性が高く、使いやすい変数デポを提供します。それはあなたのコードを非常にきれいにします。次のようなシングルトンの配列にすべての設定値があることを想像してください:

$ gb-> config ['hostname']

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

$ gb-> lang ['ENTER_USER']

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

$ template

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

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

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

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

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

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

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

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

コード付きの例、おそらく。

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

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

編集

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

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top