栄光のグローバル変数 - 栄光のグローバルクラスになります。オブジェクト指向設計を壊すと言う人もいます。
シングルトンを使用するのが理にかなっている古き良きロガー以外のシナリオを教えてください。
栄光のグローバル変数 - 栄光のグローバルクラスになります。オブジェクト指向設計を壊すと言う人もいます。
シングルトンを使用するのが理にかなっている古き良きロガー以外のシナリオを教えてください。
真実を探求する中で、シングルトンを使用する「許容できる」理由は実際にはほとんどないことを発見しました。
インターネットで何度も何度も出てくる傾向がある理由の1つは、「ロギング」クラス(あなたが言及した)の理由です。この場合、クラスの単一インスタンスの代わりに Singleton を使用できます。これは、ログ クラスは通常、プロジェクト内のすべてのクラスで何度も何度も使用する必要があるためです。すべてのクラスがこのロギング クラスを使用すると、依存関係の挿入が煩雑になります。
ロギングは、コードの実行に影響を与えないため、「許容される」シングルトンの具体例です。ロギングを無効にします。コードの実行は変わりません。有効にします、同じです。Misko はRoot Cause of Singletonsで次のように述べています。
他にも正当な理由があると確信しています。Alex Miller は、" Patterns I Hate " で、サービス ロケーターとクライアント側 UI もおそらく "受け入れられる" 選択肢であると述べています。
シングルトン候補は、次の3つの要件を満たす必要があります。
提案されたシングルトンにこれらの要件が1つか2つしかない場合は、ほとんどの場合、再設計が正しいオプションです。
たとえば、プリンタスプーラが複数の場所([印刷]メニュー)から呼び出される可能性は低いため、ミューテックスを使用して同時アクセスの問題を解決できます。
単純なロガーは、おそらく有効なシングルトンの最も明白な例ですが、これはより複雑なロギングスキームで変わる可能性があります。
起動時にのみ読み取る必要がある構成ファイルを読み取り、それらをシングルトンにカプセル化します。
共有リソースを管理する必要がある場合は、シングルトンを使用します。たとえば、プリンター スプーラー。同じリソースに対する要求の競合を避けるために、アプリケーションはスプーラーのインスタンスを 1 つだけ持つ必要があります。
またはデータベース接続またはファイルマネージャーなど。
一部のグローバル状態 (ユーザー言語、ヘルプ ファイルパス、アプリケーション パス) を格納する読み取り専用のシングルトンは合理的です。シングルトンを使用してビジネス ロジックを制御する場合は注意してください。ほとんどの場合、単一は複数になります。
データベースへの接続 (または接続のプール) の管理。
また、外部構成ファイルに関する情報を取得して保存するためにも使用します。
アプリケーション全体で共有されるリソースへのアクセスを管理する場合はシングルトンを使用する必要があり、同じクラスの複数のインスタンスが存在する可能性があると破壊的です。共有リソースへのアクセスがスレッドセーフであることを確認することは、この種のパターンが不可欠な場合の非常に良い例の1つです。
シングルトンを使用するときは、依存関係を誤って隠していないことを確認する必要があります。理想的には、シングルトン(アプリケーション内のほとんどの静的変数など)は、アプリケーションの初期化コード(C#実行可能ファイルの場合はstatic void Main()、java実行可能ファイルの場合はstatic void main())の実行中に設定され、に渡されます。それを必要とするインスタンス化される他のすべてのクラス。これは、テスト容易性を維持するのに役立ちます。
シングルトンを使用する方法の 1 つは、リソースへのアクセスを制御する単一の「ブローカー」が必要なインスタンスをカバーすることです。シングルトンは、排他的にのみ書き込み可能なファイルなどへのアクセスを仲介するため、ロガーに適しています。ロギングなどの場合、ログファイルなどへの書き込みを抽象化する方法を提供します-シングルトンなどにキャッシュメカニズムをラップできます...
また、多くのウィンドウ/スレッド/その他を備えたアプリケーションがあるが、単一の通信ポイントが必要な状況を考えてみてください。私はかつて、自分のアプリケーションで起動したいジョブを制御するために 1 つを使用していました。シングルトンは、ジョブをシリアル化し、関心のあるプログラムの他の部分にそれらのステータスを表示する責任がありました。この種のシナリオでは、シングルトンを、アプリケーション内で実行されている「サーバー」クラスのようなものと見なすことができます... HTH
シングルトンの使用は、データベースにおける多対 1 の関係と同じように考えることができると思います。オブジェクトの単一のインスタンスを操作する必要があるコードのさまざまな部分が多数ある場合は、シングルトンを使用するのが理にかなっています。
データベースまたはファイルから構成の Properties オブジェクトをロードする場合、それをシングルトンとして持つと役立ちます。サーバーの実行中に変更されない静的データを再読み取りし続ける理由はありません。
シングルトンの実用的な例はTest::Builderで見つけることができます。これは、ほぼすべての最新の Perl テスト モジュールをサポートするクラスです。Test::Builder シングルトンは、テスト プロセスの状態と履歴 (過去のテスト結果、実行されたテストの数を数えます) だけでなく、テスト出力がどこに行くのかなどを保存および仲介します。これらはすべて、異なる作成者によって作成された複数のテスト モジュールを調整して、1 つのテスト スクリプトで連携させるために必要です。
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 以外のクラスを一緒にプレイすることもできます。
データの調整と動作の柔軟性との間にプッシュとプルがあるようです。これは、データの整合性を確保するために、可能な限り最小限の動作で共有データにシングルトンを配置することで軽減できます。
共有リソース。特に PHP では、データベース クラス、テンプレート クラス、およびグローバル変数ディポ クラスです。コード全体で使用されているすべてのモジュール/クラスですべてを共有する必要があります。
これは真のオブジェクトの使用法です -> テンプレート クラスには構築中のページ テンプレートが含まれており、ページ出力に追加するモジュールによって整形、追加、変更されます。これを実現するには、単一のインスタンスとして保持する必要があり、データベースについても同じことが言えます。共有データベース シングルトンを使用すると、すべてのモジュールのクラスがクエリにアクセスし、クエリを再実行することなく取得できます。
グローバル変数デポ シングルトンは、グローバルで信頼性が高く、使いやすい変数デポを提供します。コードを大幅に整理します。次のようなシングルトンの配列にすべての構成値があると想像してください。
$gb->config['hostname']
または、次のような配列にすべての言語値を含めます。
$gb->lang['ENTER_USER']
ページのコードを実行すると、次のような成熟したものが得られます。
$template
シングルトン、$gb
置換用の lang 配列を持つシングルトンで、すべての出力がロードされて準備ができています。それらを成熟したテンプレートオブジェクトのページ値に現在存在するキーに置き換えて、それをユーザーに提供するだけです.
これの大きな利点は、好きな後処理を何でもできることです。たとえば、すべての言語値を Google 翻訳または別の翻訳サービスにパイプして元に戻し、翻訳された場所に置き換えることができます。または、必要に応じて、ページ構造またはコンテンツ文字列を置き換えることができます。
まず、Single ObjectとSingletonを区別しましょう。後者は、前者の多くの可能な実装の 1 つです。また、Single Object の問題は、Singleton の問題とは異なります。単一のオブジェクトは本質的に悪いものではなく、物事を行う唯一の方法である場合があります。要するに:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton instance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
ご覧のとおり、標準的な形式の "Singleton" パターンは、あまりテストに適していません。ただし、これは簡単に修正できます。Singleton にインターフェイスを実装させるだけです。それを「テスト可能なシングルトン」と呼びましょう:)
public class Singleton implements ISingleton {
private static Singleton instance;
private Singleton() {}
public static ISingleton instance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
インターフェイス経由で使用するため、Singleton をモックできるようになりました。請求の 1 つがなくなりました。もう 1 つのクレーム、共有グローバル状態を取り除くことができるかどうか見てみましょう。
Singleton パターンを取り除くと、その核心は遅延初期化に関するものになります。
public static ISingleton instance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
それが存在する理由のすべてです。それが単一オブジェクト パターンです。それを取り除いて、ファクトリ メソッドに入れます。たとえば、次のようになります。
public class SingletonFactory {
private static ISingleton instance;
// Knock-knock. Single Object here
public static ISingleton simpleSingleton() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Testable Singleton との違いは何ですか? これは Single Object パターンの本質であるため、 noneはありません。Singleton として実装するか、ファクトリ メソッドとして実装するか、Service Locator として実装するかは問題ではありません。まだいくつかの共有グローバル状態があります。これは、複数のスレッドからアクセスされる場合に問題になる可能性があります。simpleSingleton()
同期化して、すべてのマルチスレッドの問題に対処する必要があります。
もう一度言いますが、どのアプローチを選択しても、単一オブジェクトの価格を支払う必要があります。依存性注入コンテナーを使用すると、単一オブジェクト固有の問題に対処しなければならないフレームワークが複雑になります。
要約:
State パターンを実装するときに Singleton を使用できます (GoF book に示されている方法で)。これは、具体的な State クラスには独自の状態がなく、コンテキスト クラスの観点からアクションを実行するためです。
Abstract Factory をシングルトンにすることもできます。
誰もが言ったように、共有リソース - 特に同時アクセスを処理できないもの。
私が見た特定の例の 1 つは、Lucene Search Index Writer です。
クラスに 1 つのインスタンスがあり、そのインスタンスにグローバルなアクセス ポイントがあることを確認する場合は、Singleton デザイン パターンを使用します。
たとえば、CRUD 操作を処理するためにデータベースを必要とするアプリケーションがあるとします。データベースへの同じ接続オブジェクトを使用してデータベースにアクセスし、CRUD 操作を実行するのが理想的です。
したがって、データベース クラスが 1 つのオブジェクトを持ち、その同じオブジェクトがアプリケーション全体で使用されるようにするために、シングルトン デザイン パターンを実装します。
コンストラクターがプライベートであること、およびシングルトン クラスの単一オブジェクトへのアクセスを提供する静的メソッドを提供していることを確認してください。
プラグ可能なモジュールを扱うときに、コマンドライン パラメータをカプセル化するオブジェクトとして使用します。メイン プログラムは、ロードされるモジュールのコマンド ライン パラメータが何であるかを認識していません (また、どのモジュールがロードされているかさえ常に認識しているわけではありません)。たとえば、メインはパラメータ自体を必要としない A をロードします (したがって、なぜ追加のポインタ / 参照 / を取る必要があるのか 、よくわかりません - 汚染のように見えます)、次にモジュール X、Y、および Z をロードします。これらのうち、たとえば X と Z はパラメーターが必要 (または受け入れる) ため、コマンドライン シングルトンにコールバックして受け入れるパラメーターを伝え、実行時にユーザーが実際にパラメーターを指定したかどうかを確認するためにコールバックします。そのうちの。
多くの点で、クエリごとに 1 つのプロセスしか使用しない場合、CGI パラメーターを処理するためのシングルトンは同様に機能します (他の mod_* メソッドはこれを行わないため、そこではうまくいきません。 mod_perl などの世界に移植する場合は、mod_cgi の世界でシングルトンを使用しないでください)。
特定のインフラストラクチャの問題をシングルトンまたはグローバル変数として構成することは、非常に実用的です。これの私のお気に入りの例は、シングルトンを利用してフレームワークへの接続ポイントとして機能する依存性注入フレームワークです。
この場合、インフラストラクチャに依存して、ライブラリの使用を簡素化し、不要な複雑さを回避しています。
おそらくコード付きの例。
ここで、ConcreteRegistry はポーカー ゲームのシングルトンであり、パッケージ ツリーのすべての動作がゲームのいくつかのコア インターフェイス (つまり、モデル、ビュー、コントローラー、環境などのファサード) にアクセスできるようにします。
http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html
エド。