ゲームアーキテクチャに多くのシングルトンが含まれないようにするにはどうすればよいですか?


55

ゲームの作成にはcocos2d-xゲームエンジンを使用しています。エンジンはすでに多くのシングルトンを使用しています。誰かがそれを使用した場合、彼らはそれらのいくつかに精通している必要があります:

Director
SimpleAudioEngine
SpriteFrameCache
TextureCache
EventDispatcher (was)
ArmatureDataManager
FileUtils
UserDefault

さらに、全体で約16のクラスがあります。このページで同様のリストを見つけることができます:Cocos2d-html5 v3.0のシングルトンオブジェクトしかし、書きたいときは、もっと多くのシングルトンが必要です。

PlayerData (score, lives, ...)
PlayerProgress (passed levels, stars)
LevelData (parameters per levels and level packs)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
GameData (you may obtain some data from server to configure the game)
IAP (for in purchases)
Ads (for showing ads)
Analytics (for collecting some analytics)
EntityComponentSystemManager (mananges entity creation and manipulation)
Box2dManager (manages the physics world)
.....

なぜ私は彼らがシングルトンであると思うのですか?ゲーム内の非常に異なる場所で必要になるため、共有アクセスが非常に便利だからです。言い換えると、どこかでそれらを作成し、すべてのアーキテクチャにポインタを渡すことは非常に難しいので、私はそうしません。また、これらは私が必要とするものの一種です。いずれにせよ、いくつか必要になりますが、マルチトンパターンも使用できます。しかし、最悪の事態は、シングルトンが次の理由で最も批判されているパターンであることです。

 - bad testability
 - no inheritance available
 - no lifetime control
 - no explicit dependency chain
 - global access (the same as global variables, actually)
 - ....

ここでいくつかの考えを見つけることができます:https : //stackoverflow.com/questions/137975/what-is-so-bad-about-singletonsおよびhttps://stackoverflow.com/questions/4074154/when-should-the-singleton -使用されていないパターンは明らかです

だから、私は何か間違ったことをしていると思う。私のコードは臭いがあると思います。:)経験豊富なゲーム開発者がこのアーキテクチャ上の問題をどの程度解決できるのでしょうか。確認したいのですが、既にゲームエンジンにあるシングルトンを考慮して、ゲーム開発では30個を超えるシングルトンを使用するのが普通です。

私が必要とするこれらのすべてのクラスのインスタンスを持つシングルトンファサードを使用することを考えましたが、それらはそれぞれシングルトンではありません。これにより多くの問題が解消され、Facade自体のシングルトンは1つだけになります。しかし、この場合、別の設計上の問題があります。ファサードは神のオブジェクトになります。これも臭いがすると思う。したがって、この状況に適した設計ソリューションは見つかりません。ご意見をお聞かせください。


26
シングルトンに反論するほとんどの人は、すべてのコードにデータ/機能を伝播するために必要な作業がシングルトンを使用するよりもバグの原因になる可能性があることを認識していないようです。だから基本的に彼らは比較していない、==>私は反シングルトン主義が姿勢であると疑っているだけだ:-)まあ、シングルトンには欠陥があるので、それを避けることができれば、それに伴う問題を回避する、今はできない場合は、振り返らずにそれを行ってください。結局のところ、Cocos2dはその16のシングルトンでかなり正常に動作するようです...
GameAlchemist

6
念のため、これは特定のタスクを実行する方法に関する質問です。これは、シングルトンの賛否両論の議論を求める質問ではありません(とにかくここでは話題になりません)。さらに、コメントは、シングルトンの長所と短所に関する接線的な議論のプラットフォームとしてではなく、質問者に明確化を要求するために使用する必要があることを忘れないでください。入力を提供する場合、適切な方法は質問に対する正当な回答を提供することです。この場合、シングルトンパターンに対する賛成または反対のアドバイスでソリューションを調整できます。
ジョシュ

これらのシングルトンはグローバルにアクセスできると思いますか?これがそのパターンを回避する1つの理由です。
ピーター-モニカの復活14年

回答:


41

メインアプリケーションクラスでサービスを初期化し、コンストラクターまたは関数を使用して、それらを使用する必要があるものへのポインターとしてサービスを渡します。これは2つの理由で役立ちます。

1つは、初期化とクリーンアップの順序が単純かつ明確であることです。シングルトンでできるように、誤って1つのサービスを別の場所で初期化する方法はありません。

2つ目は、誰が何に依存しているかは明らかです。クラス宣言から、どのクラスがどのサービスを必要としているかを簡単に確認できます。

これらすべてのオブジェクトを渡すのは面倒だと思うかもしれませんが、システムがうまく設計されていれば、それはまったく悪くなく、物事をより明確にします(とにかく)。


34
+1。さらに、参照をシステムに渡す必要があるという「迷惑」は、依存関係のクリープに対抗するのに役立ちます。「すべて」に何かを渡す必要がある場合、それは設計に悪いカップリングの問題があることを示す良い兆候であり、どこからでもすべてに無差別にグローバルにアクセスするよりも、この方法のほうがはるかに明白です。
ジョシュ

2
これは実際には解決策を提供しません...だから、あなたはその中にたくさんのシングルトンオブジェクトを持つプログラムクラスを持っています、そして今シーンのどこかにあるコードの10レベルの深さはそれらのシングルトンの1つを必要とします...どのように渡されますか?
戦争14年

Wardy:なぜ彼らはシングルトンである必要があるのですか?確かに、ほとんどの場合、特定のインスタンスを渡すことがありますが、シングルトンをシングルトンにするのは、異なるインスタンスを使用できないことです。インジェクションを使用すると、あるオブジェクトに1つのインスタンスを渡し、次のオブジェクトに別のインスタンスを渡すことができます。なんらかの理由でそれをやらないからといって、できなかったわけではありません。
パララー14年

3
彼らはシングルトンではありません。これらは、init / cleanupを制御する適切に定義された所有者を持つ他のすべてのような通常のオブジェクトです。10レベルの深さでアクセスできない場合、設計上の問題がある可能性があります。すべてがレンダラー、オーディオなどにアクセスする必要があるわけではありません。これにより、デザインについて考えることができます。コードをテストする人(何??)に追加のボーナスとして、ユニットテストもはるかに簡単になります。
メガダン14年

2
はい、依存性注入(Springのようなフレームワークの有無にかかわらず)は、これを処理するための完璧なソリューションだと思います。私は、これはどこでも物事を渡すことを避けるためにどのような構造のコードより良いと思っていたもののための素晴らしい記事であることが判明:misko.hevery.com/2008/10/21/...
megadan

17

インターネットは私よりもうまくできるので、シングルトンの背後にある悪については説明しません。

私のゲームでは、大量のシングルトン/マネージャーを避けるために、サービスロケーターパターンを使用しています

コンセプトは非常にシンプルです。シングルトンとして使用していたものに到達する唯一のインターフェースのように機能するシングルトンは1つしかありません。複数のシングルトンを使用する代わりに、現在は1つだけです。

次のような呼び出し:

FlyingToasterManager.GetInstance().Toast();
SmokeAlarmManager.GetInstance().Start();

Service Locatorパターンを使用すると、次のようになります。

SvcLocator.FlyingToaster.Toast();
SvcLocator.SmokeAlarm.Start();

ご覧のとおり、すべてのシングルトンはサービスロケーターに含まれています。

より詳細な説明が必要な場合は、ロケーターパターンの使用方法についてこのページ読むことをお勧めします。

[ 編集 ]:コメントで指摘したように、サイトgameprogrammingpatterns.comには、Singletonに関する非常に興味深いセクションも含まれています。

役に立てば幸いです。


4
ここにリンクしているサイトgameprogrammingpatterns.comには、関連があると思われるシングルトンに関する章もあります。
ajp15243 14

28
サービスロケーターは、コンパイル時ではなくランタイムにすべての通常の問題を移動するシングルトンです。ただし、あなたが提案しているのは、巨大な静的レジストリであり、これはほとんど優れていますが、それでも本質的にはグローバル変数の塊です。多くのレベルが同じオブジェクトにアクセスする必要がある場合、これは単に悪いアーキテクチャの問題です。ここで必要なのは適切な依存性注入であり、現在のグローバルとは異なる名前のグローバルではありません。
メイガス

2
「適切な依存関係の注入」について詳しく説明してください。
ブルーウィザード14

17
これは実際には問題を解決しません。本質的には、多くのシングルトンが1つの巨大なシングルトンにマージされます。根本的な構造上の問題は修正されておらず、対処もされていませんが、コードは今でもきれいなだけです。
ClassicThunder 14

4
@classicthunderこれはすべての問題に対処しているわけではありませんが、複数の問題に対処しているため、Singletonsよりも大幅に改善されています。特に、作成するコードは単一のクラスに結合されるのではなく、クラスのインターフェース/カテゴリに結合されます。これで、さまざまなサービスの異なる実装を簡単に交換できます。
冗談14

12

指摘されたこれらすべての回答、コメント、記事、特にこれらの2つの素晴らしい記事を読んで、

最終的に、私は次の結論に達しました。これは、私自身の質問に対する一種の答えです。最善のアプローチは、遅延することなく、依存関係を直接渡すことです。これは明示的でテスト可能であり、グローバルアクセスはありません。非常に深く、多くの場所でデリゲートを渡す必要がある場合、間違った設計を示すことができます。しかし、プログラミングでは、1つのサイズがすべてに適合するわけではありません。したがって、それらを直接渡すのが便利でない場合がまだあります。たとえば、ゲームフレームワーク(さまざまな種類のゲームを開発するためのベースコードとして再利用されるコードテンプレート)を作成し、そこに多くのサービスを含める場合。ここでは、各サービスをどれだけ深く渡すことができるのか、また必要な場所はいくつあるのかはわかりません。特定のゲームに依存します。それでは、この種のケースでは何をしますか?Singletonの代わりにService Locatorデザインパターンを使用し、

  1. 実装に対してプログラミングするのではなく、インターフェイスに対してプログラミングします。これは、OOPプリンシパルの観点から非常に重要です。実行時に、Null Objectパターンを使用してサービスを変更したり、無効にしたりできます。これは柔軟性の観点から重要であるだけでなく、テストに直接役立ちます。無効化、モック、デコレータパターンを使用して、ロギングやその他の機能を追加することもできます。これは非常に重要なプロパティであり、シングルトンにはありません。
  2. もう1つの大きな利点は、オブジェクトの寿命を制御できることです。シングルトンでは、Lazy Initializationパターンによってオブジェクトが生成される時間のみを制御します。ただし、オブジェクトが生成されると、プログラムの終了まで存続します。ただし、場合によっては、サービスを変更したいことがあります。またはシャットダウンすることもできます。特にゲームでは、この柔軟性が非常に重要です。
  3. 複数のシングルトンを1つのコンパクトなAPIに結合/ラップします。また、サービスロケーターのようなFacadeを使用することもできます。これは、単一の操作で一度に複数のサービスを使用する場合があります。
  4. シングルトンの主な設計上の問題であるグローバルアクセスがまだあると主張するかもしれません。同意しますが、このパターンが必要になるのは、グローバルアクセスが必要な場合のみです。直接的な依存性注入が私たちの状況にとって便利ではないと考え、グローバルアクセスが必要な場合は、グローバルアクセスについて文句を言うべきではありません。ただし、この問題についても改善が可能です(上記の2番目の記事を参照)。すべてのサービスをプライベートにし、Service Locatorクラスから、サービスを使用する必要があると思われるすべてのクラスを継承できます。これにより、アクセスが大幅に制限される場合があります。シングルトンの場合、プライベートコンストラクターのために継承を使用できないことを考慮してください。

また、このパターンがUnity、LibGDX、XNAで使用されたことを考慮する必要があります。これは利点ではありませんが、これはパターンの使いやすさの証拠です。これらのエンジンは多くの賢明な開発者によって長い間開発されたものであり、エンジンの進化段階ではまだ優れたソリューションが見つからなかったと考えてください。

結論として、Service Locatorは私の質問で説明されているシナリオに非常に役立つと思いますが、可能であれば避けるべきです。経験則として-必要に応じて使用してください。

編集:1年間の作業と直接DIの使用、巨大なコンストラクターと多くの委任で問題が発生した後、私はさらに調査を行い、DIおよびService Locatorメソッドの長所と短所について話す良い記事を見つけました。実際にサービスロケーターが悪いときを説明するMartin Fowlerによるこの記事(http://www.martinfowler.com/articles/injection.html)を引用します

したがって、主な問題は、ライターの制御外のアプリケーションで使用されることを期待しているコードを書いている人々です。これらの場合、Service Locatorについての最小限の仮定でさえ問題です。

しかし、とにかく、DIを初めて使用する場合は、このhttp://misko.hevery.com/2008/10/21/dependency-injection-myth-reference-passing/の記事を読む必要があります(@megadanが指摘) 、デメテル法則(LoD)または最小知識の原則に非常に注意を払うことによって。


サービスロケーターに対する私の個人的な不寛容は、主にそれらを使用するコードをテストする試みから生じます。ブラックボックスのテストのために、コンストラクターを呼び出してテストするインスタンスを作成します。すぐに、またはメソッド呼び出しで例外がスローされる場合があります。また、不足している依存関係につながるパブリックプロパティがない場合があります。ソースにアクセスできる場合、これはささいなことかもしれませんが、それでもなお、Least Surpriseの原則(シングルトンでよくある問題)に違反しています。サービスロケーターを渡すことをお勧めします。これにより、これの一部が軽減され、賞賛されることになります。
メイガス14年

3

コードベースの一部が基礎となるオブジェクトまたは基礎クラスと見なされることは珍しくありませんが、それはライフトンがシングルトンとして決定されることを正当化するものではありません。

プログラマーは、代替アプローチをとったり、より冗長にしたり、明示的なマナーで互いにオブジェクトの関係を課すのではなく、利便性と純粋な怠inessの手段としてシングルトンパターンに依存することがよくあります。

どちらが保守しやすいかを自問してください。

あなたが私のような人であれば、実装ファイルの数百行のコードを検査するのではなく、ヘッダーファイルを開いて、コンストラクターの引数とパブリックメソッドを簡単に調べて、オブジェクトに必要な依存関係を確認できるようにすることを好みます。

オブジェクトをシングルトンにし、後でそのオブジェクトの複数のインスタンスをサポートできるようにする必要があることに気付いた場合、このクラスがコードベース全体でかなり頻繁に使用されるものである場合に必要な大幅な変更を想像してください。オブジェクトが一度しか割り当てられず、複数のインスタンスをサポートする必要が生じた場合でも、そのような心配なしに安全に行うことができます。

他の人が指摘しているように、Singletonパターンの使用は結合を難読化することもあります。これはしばしば、設計の悪さやモジュール間の高い結合性を意味します。通常、このような凝集は望ましくなく、維持が困難な脆弱なコードにつながります。


-1:この回答は、シングルトンパターンを誤って説明し、そのすべての引数は、パターンの実装が不十分な問題を説明しています。適切なシングルトンインターフェイスであり、説明したすべての問題を回避します(GoFが明示的に対処するシナリオである非単一性への移行を含む)。シングルトンの使用をリファクタリングすることが困難な場合、明示的なオブジェクト参照の使用をリファクタリングすることは、難しくはなく、難しくありません。シングルトンが何らかの理由でより多くのコード結合を引き起こす場合、それは単に間違って行われました。
セスバティン14年

1

シングルトンは有名なパターンですが、それが果たす目的とその長所と短所を知っておくとよいでしょう。

まったく関係がなければ、それは本当に理にかなっています。
コンポーネントをまったく異なるオブジェクト(強い依存関係なし)で処理でき、同じ動作を期待できる場合は、シングルトンが適切な選択である可能性があります。

一方、特定の時間にのみ使用される小さな機能が必要な場合はreferenceを渡すことお勧めします。

あなたが述べたように、シングルトンにはライフタイムのコントロールがありません。しかし、利点は、初期化が遅延していることです。
アプリケーションの有効期間中にそのシングルトンを呼び出さないと、インスタンス化されません。しかし、シングルトンを構築し、それらを使用しないことを疑います。

コンポーネントではなく、サービスのようなシングルトンを見る必要があります

シングルトンは動作し、アプリケーションを壊すことはありません。しかし、それらの欠如(あなたが与えた4つ)は、あなたが1つを作らない理由です。


1

エンジンはすでに多くのシングルトンを使用しています。誰かがそれを使用した場合、彼らはそれらのいくつかに精通している必要があります:

慣れていないことがシングルトンを避ける必要がある理由ではありません。

シングルトンが必要または避けられない理由はたくさんあります。ゲームフレームワークは、シングルトンを使用することがよくあります。これは、単一のステートフルハードウェアを持つことの避けられない結果だからです。それぞれのハンドラーの複数のインスタンスを使用してこれらのハードウェアを制御することは、意味がありません。グラフィックサーフェスは外部のステートフルハードウェアであり、グラフィックサブシステムの2番目のコピーを誤って初期化することは悲惨なことです。2つのグラフィックサブシステムは、だれがいつ描画を行うかを制御不能に上書きするため、互いに戦っています。同様に、イベントキューシステムでは、非決定的な方法でマウスイベントを取得するユーザーをめぐって争います。そのうちの1つだけが存在するステートフル外部ハードウェアを扱う場合、競合を防ぐためにシングルトンは避けられません。

シングルトンが賢明なもう1つの場所は、キャッシュマネージャーです。キャッシュは特殊なケースです。彼らは、シングルトンとまったく同じ技術を使用して生き続け、永遠に生きる場合でも、実際にはシングルトンと見なされるべきではありません。キャッシュサービスは透過的なサービスであり、プログラムの動作を変更することは想定されていません。したがって、キャッシュサービスをヌルキャッシュに置き換えても、実行速度が遅くなることを除いて、プログラムは動作します。キャッシュマネージャーがシングルトンの例外である主な理由は、アプリケーション自体をシャットダウンする前にキャッシュサービスをシャットダウンしても意味がないためです。キャッシュされたオブジェクトも破棄されるため、シングルトン。

これらは、これらのサービスがシングルトンである正当な理由です。ただし、リストしたクラスにシングルトンを適用する正当な理由はありません。

ゲーム内の非常に異なる場所で必要になるため、共有アクセスが非常に便利だからです。

これらはシングルトンの理由ではありません。これらがグローバルの理由です。また、さまざまなシステムの周りに多くのものを渡す必要がある場合、設計が不十分であることを示しています。物事をやり取りしなければならないということは、優れたオブジェクト指向設計によって防止できる高い結合を示しています。

投稿でシングルトンにする必要があると思われるクラスのリストを見るだけで、それらの半分は本当にシングルトンであってはならず、残りの半分はそもそもそこにあるべきではないように思えます。オブジェクトを渡す必要があるのは、シングルトンの実際の適切な使用例ではなく、適切なカプセル化の欠如によるものと思われます。

クラスを少しずつ見てみましょう。


PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)

ちょうどクラス名から、これらのクラスは、Anemic Domain Modelアンチパターンのような匂いがすると言うでしょう。貧血オブジェクトは通常、多くのデータを渡す必要があるため、結合が増加し、残りのコードが面倒になります。また、貧血クラスは、オブジェクトの方向を使用して詳細をカプセル化するのではなく、おそらく手続き的に考えているという事実を隠しています。


IAP (for in purchases)
Ads (for showing ads)

これらのクラスがシングルトンである必要があるのはなぜですか?これらは短命のクラスである必要があり、ユーザーが購入を完了したとき、または広告を表示する必要がなくなったときに分解して解体する必要があります。


EntityComponentSystemManager (mananges entity creation and manipulation)

言い換えれば、コンストラクターとサービス層ですか?そもそもなぜこのクラスには明確な境界も目的もないのでしょうか?


PlayerProgress (passed levels, stars)

プレーヤーの進行状況がPlayerクラスとは別になっているのはなぜですか?Playerクラスは、自身の進行状況を追跡する方法を知っている必要があります。責任の分離のためにPlayerクラスとは異なるクラスで進行状況の追跡を実装する場合、PlayerProgressはPlayerの背後にある必要があります。


Box2dManager (manages the physics world)

このクラスが実際に何をするのか知らずに、これについてこれ以上コメントすることはできませんが、1つはっきりしているのは、このクラスの名前が貧弱であることです。


Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)

これは、ソーシャル接続オブジェクトが実際にはあなたのオブジェクトではないため、Singletonが妥当な唯一のクラスのようです。ソーシャルコネクションは、リモートサービスに存在する外部オブジェクトの単なる影です。つまり、これは一種のキャッシュです。キャッシング部分と外部サービスのサービス部分を明確に分離してください。後者の部分はシングルトンである必要はありません。

これらのクラスのインスタンスの受け渡しを回避する1つの方法は、メッセージの受け渡しを使用することです。これらのクラスのインスタンスを直接呼び出すのではなく、Analytic and SocialConnectionサービスに宛てたメッセージを非同期で送信すると、これらのサービスはこれらのメッセージを受信して​​メッセージに作用するようにサブスクライブされます。イベントキューは既にシングルトンであるため、呼び出しをトランポリンすることにより、シングルトンと通信するときに実際のインスタンスを渡す必要がなくなります。


-1

私にとってシングルトンは常に悪いです。

私のアーキテクチャでは、ゲームクラスが管理するGameServicesコレクションを作成しました。必要に応じて、このコレクションの追加と削除を検索できます。

上記の例では、何かがグローバルスコープ内にあると言うことは、基本的に「このhtingは神であり、マスターがない」と言っていることです。それらのほとんどはヘルパークラスまたはGameServicesであり、その場合はゲームが責任を負います。

しかし、それは私のエンジンでの私の設計であり、あなたの状況は異なるかもしれません。

もっと直接的に言えば...唯一の本当のシングルトンは、コードのあらゆる部分を所有するゲームです。

これは、質問の主観的な性質に基づいた答えであり、純粋に私の意見に基づいています。すべての開発者にとって正しいとは限りません。

編集:要求に応じて...

わかりました。これは現時点では私のコードを目の前に置いていないためです。しかし、ゲームの根本にあるのはGameクラスであり、これはまさにシングルトンです...

だから私は次を追加しました...

class Game
{
   IList<GameService> Services;

   public T GetService<T>() where T : GameService
   {
       return Services.FirstOrDefatult(s => s is T);
   }
}

また、プログラム全体からのすべてのシングルトンを含むシステムクラス(静的)があります(ゲームオプションと構成オプションがほとんど含まれていません)。

public static class Sys
{
    // only the main game assembly can set this (done by the game ctor)
    // anything can get
    public static Game Game { get; internal set; }
}

そして使い方...

どこからでも私は次のようなことができます

var tService = Sys.Game.getService<T>();
tService.Something();

このアプローチは、通常「シングルトン」と見なされるゲームの「所有」のものであり、必要に応じて基本のGameServiceクラスを拡張できることを意味します。IUpdateableのようなインターフェイスを使用して、特定の状況で必要に応じて、ゲームがそれを実装するサービスをチェックして呼び出します。

また、より具体的なシーンシナリオのために他のサービスを継承/拡張するサービスもあります。

例えば ​​...

物理シミュレーションを処理する物理サービスがありますが、シーンによっては物理シミュレーションに若干異なる動作が必要になる場合があります。


1
ゲームサービスは1回だけインスタンス化されるべきではありませんか?もしそうなら、それはクラスレベルで実施されていない単なるシングルトンパターンであり、適切に実装されたシングルトンよりも最悪です。
GameAlchemist 14

2
@Wardy私はあなたが何をしたのか明確に理解していませんが、私が説明したSingleton-Facadeのように聞こえ、GODオブジェクトになるはずですよね?
ナレク14

10
これは、ゲームレベルで実装されたシングルトンパターンにすぎません。したがって、基本的に最も興味深いのは、Singletonを使用していないふりをすることです。上司がアンチパターン教会の出身である場合に役立ちます。
GameAlchemist 14

2
これがシングルトンを使用することとどのように効果的に異なるかはわかりません。このタイプの単一の既存サービスにどのように具体的にアクセスするかで唯一の違いはありませんか?すなわちvar tService = Sys.Game.getService<T>(); tService.Something();getService部品をスキップして直接アクセスする代わりに?
クリスチャン14

1
@クリスチャン:確かに、それはまさにサービスロケーターのアンチパターンです。どうやら、このコミュニティはまだそれが推奨される設計パターンだと考えています。
メイガス

-1

シングルトンの反対者がしばしば考慮しないシングルトン排除の重要な要素は、シングルトンがインスタンス値に取って代わるときにオブジェクトがカプセル化する非常に複雑な状態に対処するための特別なロジックを追加することです。実際、シングルトンをインスタンス変数で置き換えた後のオブジェクトの状態を定義することさえ困難な場合がありますが、シングルトンをインスタンス変数で置き換えるプログラマーはそれに対処する準備をする必要があります。

たとえば、Java HashMapと.NETを比較しますDictionary。どちらも非常に似ていますが、重要な違いがあります。Java HashMapはハードコードされてequalsおりhashCode(シングルトンコンパレーターを効果的に使用します)、. NETはのDictionaryインスタンスをIEqualityComparer<T>コンストラクターパラメーターとして受け入れることができます。を維持するための実行時コストIEqualityComparerは最小限ですが、それがもたらす複雑さはそうではありません。

Dictionaryインスタンスはをカプセル化するためIEqualityComparer、その状態は実際に含まれるキーとそれらの関連する値との間マッピングだけでなく、キーとコンパレータとそれらの関連する値によって定義される等価セットマッピングです。2つのインスタンス場合d1d2Dictionary同じに保持参照IEqualityComparer、新しいインスタンスを生成することが可能となるd3ような任意のそれxd3.ContainsKey(x)等しくなりますd1.ContainsKey(x) || d2.ContainsKey(x)。ただし、場合、d1およびd2異なる任意の平等する比較器への参照を保持することができる、一般的にその条件が成立していることを確認するようにそれらをマージする方法はありません。

シングルトンを排除するためにインスタンス変数を追加しますが、それらのインスタンス変数によって暗示される可能性のある新しい状態を適切に説明しないと、シングルトンの場合よりも脆弱なコードが作成される可能性があります。すべてのオブジェクトインスタンスに個別のフィールドがあり、それらがすべて同じオブジェクトインスタンスを識別しない限り、物事がひどく誤動作する場合、購入したすべての新しいフィールドは、物事がうまくいかない可能性のある方法の増加です。


1
これは間違いなく議論の興味深い側面ですが、OPが実際に尋ねた質問に実際に対処しているとは思いません。
ジョシュ

1
@JoshPetrie:オリジナルの投稿(たとえばMagusによる)のコメントは、シングルトンの使用を避けるために参照を実行するランタイムコストはそれほど大きくないと示唆しました。そのため、シングルトンを回避するために、すべてのオブジェクトに参照をカプセル化する意味論的なコストを考慮します。安価で簡単に回避できる場合は、シングルトンを回避する必要がありますが、シングルトンを明白に必要としないが、何かの複数のインスタンスが存在するとすぐに壊れるコードは、シングルトンを使用するコードよりも悪い場合があります。
supercat

2
回答は、質問に直接回答するためのコメントに対処するためのものではありません。
ClassicThunder 14年

@ClassicThunder:編集の方が好きですか?
supercat 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.