シングルトンの使用(および過剰使用)の問題については、最近多くの議論がありました。私もキャリアの早い段階でそうした人々の1人でした。私は今問題が何であるかを見ることができます、そして、それでも、私がニースの代替案を見ることができない多くの場合がまだあります-そして、多くの反シングルトンの議論は実際にそれを提供しません。
以下は、私が関わった最近の主要なプロジェクトの実際の例です。
アプリケーションは、頻繁に更新されないサーバー状態からの大量のデータを使用する多くの個別の画面とコンポーネントを備えたシッククライアントでした。このデータは基本的にシングルトンの「マネージャー」オブジェクト、つまり恐ろしい「グローバル状態」にキャッシュされていました。アプリ内の1か所にデータを保存して同期し、開いた新しい画面では、サーバーからのさまざまなサポートデータを繰り返し要求することなく、そこから必要なもののほとんどをクエリできるようにするというアイデアでした。絶えずサーバーに要求すると帯域幅がかかりすぎます-そして私は毎週数千ドルの追加のインターネット請求を話しているので、それは受け入れられませんでした。
基本的にこの種のグローバルデータマネージャーキャッシュオブジェクトを用意する以外に、ここで適切なアプローチはありますか?もちろん、このオブジェクトは公式に「シングルトン」である必要はありませんが、概念的には1つであることは理にかなっています。ここでニースのクリーンな代替とは何ですか?
ここでsingle instancesと Singleton design pattern を区別することが重要です。
単一インスタンスは単なる現実です。ほとんどのアプリは、一度に1つの構成、一度に1つのUI、一度に1つのファイルシステムなどでのみ機能するように設計されています。保持する状態やデータがたくさんある場合は、確かにインスタンスを1つだけにし、それを可能な限り長く維持することをお勧めします。
シングルトン設計パターンは、単一インスタンスの非常に具体的なタイプであり、具体的には次のとおりです。
この特定の設計上の選択が原因で、パターンがいくつかの潜在的な長期的な問題を引き起こすのは次のとおりです。
これらの症状のいずれも、実際には単一インスタンスに固有のものではなく、シングルトンパターンのみです。
代わりに何ができますか?シングルトンパターンを使用しないでください。
質問からの引用:
アプリ内の1か所にデータを保存して同期し、開いた新しい画面では、サーバーからのさまざまなサポートデータを繰り返し要求することなく、そこから必要なもののほとんどをクエリできるようにするというアイデアでした。絶えずサーバーに要求すると帯域幅がかかりすぎます-そして私は毎週数千ドルの追加のインターネット請求を話しているので、それは受け入れられませんでした。
この概念には名前がありますが、ヒントとしてはわかりません。 cache と呼ばれます。凝ったものにしたい場合は、それを「オフラインキャッシュ」またはリモートデータのオフラインコピーと呼ぶことができます。
キャッシュはシングルトンである必要はありません。 may複数のキャッシュインスタンスで同じデータをフェッチしないようにする場合は、単一のインスタンスである必要があります。しかし、だからといって、実際にすべてを全員に公開するにする必要があるわけではありません。
最初に、キャッシュのさまざまな機能領域を個別のインターフェイスに分離します。たとえば、Microsoft Accessに基づいて世界最悪のYouTubeクローンを作成していたとします。
MSAccessCache ▲ | + ----------------- + -------- --------- + | | | IMediaCache IProfileCache IPageCache | | | | | | VideoPage MyAccountPage MostPopularPage
ここには、いくつかのinterfacesが記述されていますspecific特定のクラスがアクセスする必要があるデータのタイプ-メディア、ユーザープロファイル、および静的ページ(フロントページなど)。これらはすべて実装 1メガキャッシュ分ですが、代わりにインターフェースを受け入れるように個々のクラスを設計するため、それらが持つインスタンスの種類は関係ありません。プログラムの起動時に物理インスタンスを1回初期化し、その後、コンストラクターとパブリックプロパティを介して(特定のインターフェイス型にキャストされた)インスタンスを渡し始めるだけです。
ちなみに、これは Dependency Injection と呼ばれます。 独自にインスタンス化するの代わりに、一般的なクラス設計呼び出し元からの依存関係を受け入れるである限り、Springまたは特別なIoCコンテナを使用する必要はありません。 グローバル状態の参照。
インターフェースベースのデザインを使用する理由3つの理由:
コードを読みやすくします。インターフェースから正確に明確に理解できますどのデータ依存クラスが依存するか。
Microsoft Accessがデータバックエンドに最適ではないことに気付いた場合は、それをより良いものに置き換えることができます。たとえば、SQL Serverとしましょう。
SQL Serverがメディアにとって最良の選択ではないことに気付いた場合、具体的に、実装を分割することができますシステムの他の部分に影響を与えることなく。ここで、抽象化の真の力が発揮されます。
さらに一歩進めたい場合は、Spring(Java)やUnity(.NET)などのIoCコンテナー(DIフレームワーク)を使用できます。ほとんどすべてのDIフレームワークは独自のライフタイム管理を行い、特定のサービスを単一のインスタンスとして定義できるようにします(しばしば「シングルトン」と呼びますが、それは親しみやすさのためだけです)。基本的にこれらのフレームワークは、手動でインスタンスを渡すというサルの作業のほとんどを節約しますが、厳密に必要なわけではありません。 このデザインを実装するために特別なツールは必要ありません。
完全を期すために、上記のデザインも実際には理想的ではないことを指摘しておきます。キャッシュを処理しているときは(現状のまま)、実際には完全に別のlayerが必要です。つまり、次のようなデザインです。
+-IMediaRepository | キャッシュ(汎用)--------------- +-IProfileRepository ▲| | +-IPageRepository + ----------------- + ----------------- + | | | IMediaCache IProfileCache IPageCache | | | | | | VideoPage MyAccountPage MostPopularPage
これの利点は、リファクタリングすることに決めた場合でも、Cache
インスタンスを分割する必要がないことです。 IMediaRepository
の代替実装にメディアを供給するだけで、メディアの保存方法を変更できます。これがどのように適合するかを考えると、キャッシュの物理インスタンスが1つしか作成されないため、同じデータを2回フェッチする必要がないことがわかります。
これは、世界中のすべてのソフトウェアが、これらの高い凝集性と疎結合の厳しい基準に合わせて設計される必要があると言っているわけではありません。それは、プロジェクトの規模と範囲、チーム、予算、期限などによって異なります。ただし、(シングルトンの代わりに使用するための)最適な設計を尋ねる場合は、これがそれです。
追伸他の人が述べたように、依存クラスがcacheを使用していることを認識することはおそらく最善の考えではありません-これは、単に気にする必要のない実装の詳細です。とはいえ、全体的なアーキテクチャは上記の図と非常によく似ていますが、個々のインターフェースをCachesとは呼びません。代わりに、それらにServicesまたは同様の名前を付けます。
あなたが与える場合、それはシングルトンの使用は問題ではないようですが、問題の症状-より大きな、建築上の問題です。
画面がキャッシュオブジェクトにデータを照会するのはなぜですか?キャッシングはクライアントに対して透過的である必要があります。データを提供するための適切な抽象化が必要であり、その抽象化の実装はキャッシングを利用する場合があります。
問題は、システムの部分間の依存関係が正しく設定されていない可能性が高く、これはおそらく体系的です。
画面がデータを取得する場所を知っている必要があるのはなぜですか?画面にデータの要求を満たすことができるオブジェクトが用意されていないのはなぜですか(キャッシュの背後に隠されています)。多くの場合、画面作成の責任は一元化されていないため、依存関係を注入する明確なポイントがありません。
繰り返しますが、私たちは大規模な建築と設計の問題に目を向けています。
また、オブジェクトのlifetimeは、オブジェクトの検索方法から完全に分離できることを理解することはvery重要です使用する。
キャッシュは、アプリケーションの存続期間を通じて(有用であるため)存続する必要があるため、オブジェクトの存続期間はシングルトンの存続期間になります。
しかし、シングルトンの問題(少なくとも静的クラス/プロパティとしてのシングルトンの一般的な実装)は、それを使用する他のクラスがどのようにしてそれを見つけるのかということです。
静的なシングルトン実装では、必要に応じて単純に使用するのが慣例です。しかし、これは依存関係を完全に隠し、2つのクラスを密結合します。
クラスへの依存関係を提供する場合、その依存関係は明示的であり、使用するすべてのクラスが、使用できるコントラクトであることを知っている必要があります。
私はこの質問について 章全体 を書きました。主にゲームのコンテキストで使用されますが、そのほとんどはゲーム以外にも適用されます。
tl; dr:
ギャングオブフォーシングルトンパターンは、2つのことを実行します。どこからでもオブジェクトに簡単にアクセスできるようにし、そのインスタンスを1つだけ作成できるようにします。 99%の時間、あなたが気にするのはその前半だけであり、それを取得するために後半に沿ってカートすることは不必要な制限を追加します。
それだけでなく、便利なアクセスを提供するためのより良いソリューションがあります。オブジェクトをグローバルにすることは、それを解決するための核となるオプションであり、カプセル化を簡単に破壊することができます。グローバルの悪い点はすべてシングルトンに完全に当てはまります。
同じオブジェクトに触れる必要のあるコード内の場所がたくさんあるという理由だけでそれを使用している場合は、それなしでそれらのオブジェクトにjustを与えるより良い方法を見つけてくださいコードベース全体に公開します。その他の解決策:
完全に破棄します。状態がなく、ヘルパー関数のバッグにすぎないシングルトンクラスをたくさん見ました。それらはインスタンスをまったく必要としません。それらを静的関数にするか、関数が引数として受け取るクラスの1つに移動します。 123.Abs()
を実行できれば、特別なMath
クラスは必要ありません。
それを渡します。メソッドが他のオブジェクトを必要とする場合の単純な解決策は、単にそれを渡すことです。いくつかのオブジェクトを渡すことには何の問題もありません。
基本クラスに入れます。特別なオブジェクトへのアクセスが必要なクラスがたくさんあり、それらが基本クラスを共有している場合は、そのオブジェクトをベースのメンバーにします。作成するときは、オブジェクトを渡します。これで、派生オブジェクトはすべて、必要なときに取得できます。保護する場合は、オブジェクトがカプセル化されたままであることを確認します。
それ自体がグローバルな状態ではありません。
本当に心配する必要があるのはglobal mutable state
。定常状態は副作用の影響を受けないため、問題は少なくなります。
シングルトンの主な懸念は、カップリングが追加され、テストなどが困難になることです。シングルトンを別のソース(工場など)から取得することで、結合を減らすことができます。これにより、コードを特定のインスタンスから切り離すことができます(ただし、ファクトリーとの結合が強くなります(ただし、少なくともファクトリーは、異なるフェーズの代替実装を持つことができます))。
あなたの状況では、シングルトンが実際にインターフェイスを実装している限り、代替手段を他の状況で使用できるようにして、それでうまくいくと思います。
しかし、シングルトンのもう1つの大きな欠点は、いったんインプレースでそれらをコードから削除して別のものに置き換えることは、本当に困難な作業になることです(その結合が再び存在します)。
// Example from 5 minutes (con't be too critical)
class ServerFactory
{
public:
// By default return a RealServer
ServerInterface& getServer();
// Set a non default server:
void setServer(ServerInterface& server);
};
class ServerInterface { /* define Interface */ };
class RealServer: public ServerInterface {}; // This is a singleton (potentially)
class TestServer: public ServerInterface {}; // This need not be.
じゃあ何?誰もそれを言わなかったので:Toolbox。これは、グローバル変数が必要な場合です。
シングルトンabuseは、別の角度から問題を見ることで回避できます。アプリケーションがクラスのインスタンスを1つだけ必要とし、アプリケーションが起動時にそのクラスを構成するとします。なぜクラス自体がシングルトンである必要があるのでしょうか。アプリケーションがこの種の動作を必要とするため、アプリケーションがこの責任を負うことは非常に論理的です。コンポーネントではなく、アプリケーションをシングルトンにする必要があります。次に、アプリケーションは、コンポーネント固有のコードを使用できるように、コンポーネントのインスタンスを作成します。アプリケーションがそのようなコンポーネントをいくつか使用すると、それらをツールボックスと呼ばれるものに集約できます。
簡単に言うと、アプリケーションのツールボックスは、それ自体の構成またはアプリケーションの起動メカニズムによる構成を可能にするシングルトンです...
public class Toolbox {
private static Toolbox _instance;
public static Toolbox Instance {
get {
if (_instance == null) {
_instance = new Toolbox();
}
return _instance;
}
}
protected Toolbox() {
Initialize();
}
protected void Initialize() {
// Your code here
}
private MyComponent _myComponent;
public MyComponent MyComponent() {
get {
return _myComponent();
}
}
...
// Optional: standard extension allowing
// runtime registration of global objects.
private Map components;
public Object GetComponent (String componentName) {
return components.Get(componentName);
}
public void RegisterComponent(String componentName, Object component)
{
components.Put(componentName, component);
}
public void DeregisterComponent(String componentName) {
components.Remove(componentName);
}
}
しかし、何だと思いますか?シングルトンです!
多分それは混乱が始まる場所です。
私にとってsingletonは、常に単一のインスタンスのみを持つように強制されたオブジェクトです。インスタンス化する必要なく、いつでもどこでもアクセスできます。それが static
に密接に関連している理由です。比較のために、static
はインスタンスではないことを除いて、基本的に同じものです。自動的に割り当てられるため、インスタンス化する必要はありません。そして、それは問題を引き起こす可能性があります。
私の経験から、シングルトンのstatic
を置き換えるだけで、中規模のパッチワークバッグプロジェクトの多くの問題が解決しました。これは、設計の悪いプロジェクトに使用方法があることを意味します。 discussion が多すぎると思います singleton pattern が seful であるかどうか、そしてそれが本当に badであるかどうかは本当に議論できません 。しかし、それでも 一般に、静的メソッドよりもシングルトンを支持する良い引数 があります。
シングルトンが悪いと私が確信している唯一のことは、良い慣行を無視してそれらを使うときです。それは確かに扱いにくいものです。しかし、悪い習慣はどのパターンにも適用できます。そして、私が知っていることは、それを言うことはあまりにも一般的です...私はそれにあまりにも多くがあることを意味します。
簡単に言えば、global varsのように、 シングルトンは still 常に回避する必要があります です。特に彼らが過度に虐待されているからです。しかし、グローバル変数は常に回避できるわけではなく、最後のケースではそれらを使用する必要があります。
とにかく、ツールボックス以外にも他の多くの提案があり、ツールボックスと同様に、それぞれにアプリケーションがあります...
シングルトンについて読んだばかりの最高の記事 は、代替案として Service Locator を提案しています。私にとって、それは基本的に "Static Toolbox"です。つまり、Service Locatorをシングルトンにすると、ツールボックスができます。もちろん、これはシングルトンを回避するという当初の提案に反しますが、それはシングルトンの問題を強制するためだけのものであり、それ自体はパターンではなく、その使用方法です。
その他 提案 Factory Pattern 代替案として。これは私が同僚から聞いた最初の選択肢であり、 global var として使用するためにすぐに排除しました。それは確かにその使用法を持っていますが、シングルトンもそうです。
上記の両方の選択肢は良い選択肢です。しかし、それはすべてあなたの使い方に依存します。
さて、シングルトンは絶対に避けなければならないという意味は間違っています...
(抽象化またはサブクラス化するための)不可能性は確かにそこにありますが、それは何ですか?そのためのものではありません。 私が知ることができる である限り、インターフェイスへの不可能性はありません。高いカップリングも存在する可能性がありますが、それはそれが一般的に使用されている方法だからです。 する必要はありません 。実際、結合自体はシングルトンパターンとは関係ありません。これは明確化されており、テストの難しさも解消されています。並列化の難しさに関しては、それは言語とプラットフォームに依存するので、繰り返しになりますが、パターンの問題ではありません。
シングルトンに賛成と反対の両方で2が使用されているのをよく見ます。 Webキャッシュ(私の場合)およびログサービス。
ロギング 一部は議論する は、完璧なシングルトンの例です。
- リクエスタには、ログへのリクエストを送信するための既知のオブジェクトが必要です。これは、グローバルなアクセスポイントを意味します。
- ロギングサービスは複数のリスナーが登録できる単一のイベントソースであるため、インスタンスは1つだけで十分です。
- 異なるアプリケーションが異なる出力デバイスにログを記録する場合がありますが、それらのリスナーを登録する方法は常に同じです。すべてのカスタマイズはリスナーを介して行われます。クライアントは、テキストがログに記録される方法または場所を知らなくても、ログを要求できます。したがって、すべてのアプリケーションがロギングサービスをまったく同じ方法で使用します。
- どのアプリケーションでも、ロギングサービスのインスタンスを1つだけ使用できるようにする必要があります。
- オブジェクトは、再利用可能なコンポーネントを含むロギングリクエスタにすることができるため、特定のアプリケーションに結合しないでください。
他の人は、ログサービスを実際には1つのインスタンスだけにすべきではないと最終的に気づくと、ログサービスを拡張することが困難になると主張します。
まあ、私は両方の引数が有効であると言います。ここでも、問題はシングルトンパターンではありません。リファクタリングが実行可能なリスクであるかどうかは、アーキテクチャの決定と重み付けにあります。通常、リファクタリングが最後に必要な修正手段である場合、これはさらに問題になります。
シングルトンデザインパターンに関する私の主な問題は、アプリケーションに適切な単体テストを作成することが非常に難しいことです。
この「マネージャー」への依存関係を持つすべてのコンポーネントは、シングルトンインスタンスを照会することによってそうします。そのようなコンポーネントの単体テストを作成する場合は、このシングルトンインスタンスにデータを挿入する必要がありますが、これは簡単ではない場合があります。
一方、「マネージャー」がコンストラクターパラメーターを介して依存コンポーネントに注入され、コンポーネントがマネージャーの具象型を認識していない場合、マネージャーが実装するインターフェースまたは抽象基本クラスのみを認識している場合は、ユニットtestは、依存関係をテストするときに、マネージャーの代替実装を提供できます。
IOCコンテナを使用して、アプリケーションを構成するコンポーネントを構成およびインスタンス化する場合、IOCコンテナを構成して、 「マネージャー」により、グローバルアプリケーションキャッシュを制御する同じ1つのインスタンスのみを実現できます。
しかし、単体テストを気にしない場合は、シングルトンの設計パターンで完全にうまくいく可能性があります。 (とにかく私はそれをしません)
シングルトンは根本的な方法ではありませんbad設計のコンピューティングには良いものも悪いものもあるという意味で。正しいことができる(期待される結果が得られる)か、そうでないかです。コードがより明確に、またはより効率的になると、それも役立つかどうかにかかわらず、.
シングルトンが役立つ1つのケースは、シングルトンが本当に一意のエンティティを表す場合です。ほとんどの環境では、データベースは一意であり、実際にはデータベースは1つだけです。そのデータベースへの接続は、特別な権限が必要なため、または複数の接続タイプを通過するため、複雑になる場合があります。この接続をシングルトンに編成することは、おそらくこの理由だけでも多くの意味があります。
ただし、シングルトンが本当にグローバル変数ではなくシングルトンであることを確認する必要もあります。これは、単一の一意のデータベースが実際に4つのデータベースであり、それぞれが本番、ステージング、開発、およびテストフィクスチャ用である場合に重要です。データベースシングルトンは、接続する必要があるものを特定し、そのデータベースの単一のインスタンスを取得し、必要に応じて接続し、呼び出し元に返します。
シングルトンが実際にはシングルトンではない場合(これは、ほとんどのプログラマが混乱する場合)、遅延してインスタンス化されたグローバルであり、正しいインスタンスを挿入する機会はありません。
適切に設計されたシングルトンパターンのもう1つの便利な機能は、観察できないことが多いことです。発信者は接続を要求します。それを提供するサービスは、プールされたオブジェクトを返すことができます。または、テストを実行している場合は、呼び出し元ごとに新しいオブジェクトを作成するか、代わりにモックオブジェクトを提供できます。
実際のオブジェクトを表すシングルトンパターンの使用は完全に許容されます。私はiPhone向けに書いていますが、Cocoa Touchフレームワークにはたくさんのシングルトンがあります。アプリケーション自体は、クラスUIApplication
のシングルトンで表されます。あなたがいるアプリケーションは1つしかないので、シングルトンでそれを表すのが適切です。
シングルトンをデータマネージャークラスとして使用しても、適切に設計されていれば問題ありません。データプロパティのバケットである場合、それはグローバルスコープと同じです。それがゲッターとセッターのセットである場合、それはより良いですが、それでも素晴らしいとは言えません。リモートデータのフェッチ、キャッシュ、セットアップ、ティアダウンなど、データへのすべてのインターフェイスを実際に管理するクラスである場合...これは非常に便利です。
シングルトンは、サービス指向アーキテクチャをプログラムに投影したものにすぎません。
APIは、プロトコルレベルのシングルトンの例です。基本的にシングルトンであるものを介してTwitter、Googleなどにアクセスします。では、なぜプログラム内でシングルトンが悪くなるのでしょうか?
それはあなたがプログラムをどう思うかによる。プログラムを、ランダムにバインドされたキャッシュされたインスタンスではなく、サービスの社会として考える場合、シングルトンは完全に理にかなっています。
シングルトンはサービスアクセスポイントです。おそらく非常に洗練された内部アーキテクチャを隠す、緊密にバインドされた機能ライブラリへのパブリックインターフェイス。
したがって、シングルトンは工場とは異なるものとは見なしません。シングルトンには、コンストラクタパラメータを渡すことができます。たとえば、考えられるすべての選択メカニズムに対してデフォルトプリンタを解決する方法を知っているコンテキストによって作成できます。テストのために、独自のモックを挿入できます。そのため、非常に柔軟にすることができます。
キーはプログラムの内部にあり、実行して少しの機能が必要なときは、サービスが稼働していてすぐに使用できるという確信を持ってシングルトンにアクセスできます。これは、準備が整っていると見なされるために状態マシンを通過する必要があるプロセスで開始するさまざまなスレッドがある場合に重要です。
通常、クラスXxxService
の周りにシングルトンをラップするXxx
クラスをラップします。シングルトンはクラスXxx
にはまったく含まれていません。別のクラスXxxService
に分離されています。可能性は低いですが、Xxx
には複数のインスタンスが存在する可能性がありますが、各システムでグローバルにアクセスできる1つのXxx
インスタンスを保持したいためです。 XxxService
は、懸念事項を適切に分離します。 Xxx
はシングルトンポリシーを適用する必要はありませんが、必要に応じてXxx
をシングルトンとして使用できます。
何かのようなもの:
//XxxService.h:
/**
* Provide singleton wrapper for Xxx object. This wrapper
* can be autogenerated so is not made part of the object.
*/
#include "Xxx/Xxx.h"
class XxxService
{
public:
/**
* Return a Xxx object as a singleton. The double check
* singleton algorithm is used. A 0 return means there was
* an error. Developers should use this as the access point to
* get the Xxx object.
*
* <PRE>
* @@ #include "Xxx/XxxService.h"
* @@ Xxx* xxx= XxxService::Singleton();
* <PRE>
*/
static Xxx* Singleton();
private:
static Mutex mProtection;
};
//XxxService.cpp:
#include "Xxx/XxxService.h" // class implemented
#include "LockGuard.h"
// CLASS SCOPE
//
Mutex XxxService::mProtection;
Xxx* XxxService::Singleton()
{
static Xxx* singleton; // the variable holding the singleton
// First check to see if the singleton has been created.
//
if (singleton == 0)
{
// Block all but the first creator.
//
LockGuard lock(mProtection);
// Check again just in case someone had created it
// while we were blocked.
//
if (singleton == 0)
{
// Create the singleton Xxx object. It's assigned
// to a temporary so other accessors don't see
// the singleton as created before it really is.
//
Xxx* inprocess_singleton= new Xxx;
// Move the singleton to state online so we know that is has
// been created and it ready for use.
//
if (inprocess_singleton->MoveOnline())
{
LOG(0, "XxxService:Service: FAIL MoveOnline");
return 0;
}
// Wait until the module says it's in online state.
//
if (inprocess_singleton->WaitTil(Module::MODULE_STATE_ONLINE))
{
LOG(0, "XxxService:Service: FAIL move to online");
return 0;
}
// The singleton is created successfully so assign it.
//
singleton= inprocess_singleton;
}// still not created
}// not created
// Return the created singleton.
//
return singleton;
}// Singleton
各画面で、コンストラクターのManagerを受け取ります。
アプリを起動すると、マネージャーのインスタンスが1つ作成され、渡されます。
これはInversion of Controlと呼ばれ、構成の変更時やテスト時にコントローラーを交換することができます。さらに、アプリケーションの複数のインスタンスまたはアプリケーションの一部を並行して実行できます(テストに適しています)。最後に、あなたのマネージャーは所有するオブジェクト(スタートアップクラス)で死にます。
したがって、アプリをツリーのように構造化し、上にあるものはそれらの下に使用されるすべてを所有します。メッシュのようなアプリを実装しないでください。誰もがみんなを知っていて、グローバルな方法でお互いを見つけます。
IMO、あなたの例は大丈夫ですね。次のように因数分解することをお勧めします。各(および各背後の)データオブジェクトのキャッシュオブジェクト。キャッシュオブジェクトとdbアクセサーオブジェクトのインターフェイスは同じです。これにより、コードの内外でキャッシュをスワップすることができます。さらに、簡単な拡張ルートを提供します。
グラフィック:
DB
|
DB Accessor for OBJ A
|
Cache for OBJ A
|
OBJ A Client requesting
DBアクセサーとキャッシュは、同じオブジェクトまたはダックタイプを継承して、同じオブジェクトのように見えるようにすることができます。プラグイン/コンパイル/テストでき、それでも動作する限り。
これにより、いくつかのUber-Cacheオブジェクトにアクセスして変更しなくても、新しいキャッシュを追加できるようになります。 YMMV。 IANAL。等。
最初の質問、アプリケーションに多くのバグを見つけましたか?おそらく、キャッシュを更新するのを忘れている、またはキャッシュが悪い、または変更が難しいと感じましたか? (色も変更しない限り、アプリでサイズが変更されないことを覚えています...ただし、色を変更してサイズを維持することはできます)。
あなたがすることは、そのクラスを持つことですが、すべての静的メンバーを削除してください。 Okこれは必須ではありませんが、私はそれをお勧めします。実際には、通常のクラスのようにクラスを初期化し、ポインタを渡すだけです。ClassIWant.APtr()。LetMeChange.ANYTHINGATALL() .andhave_no_structure()
作業量は増えますが、実際には混乱が少なくなります。もはやグローバルではなくなったため、今ではできないものを変更すべきではない場所。私のマネージャークラスはすべて通常のクラスです。それをそのように扱ってください。
パーティーには少し遅れますが、とにかく。
シングルトンは、他と同様に、ツールボックス内のツールです。うまくいけば、1つのハンマーだけでなく、ツールボックスにも多くの情報が含まれるようになります。
このことを考慮:
public void DoSomething()
{
MySingleton.Instance.Work();
}
対
public void DoSomething(MySingleton singleton)
{
singleton.Work();
}
DoSomething(MySingleton.instance);
1番目のケースは、高結合などにつながります。 2番目の方法には問題がありません@Aaronaughtが説明している限りです。使い方のすべて。