XNAフレームワークの調査からこの質問に来ていますが、一般的な理解が必要です。
ISomeService someService = (ISomeService)Game.GetServices(typeof(ISomeService));
次に、インターフェイスにある関数/プロパティを使用して何かを実行します。
someService.DoSomething(); // let's say not a static method but doesn't matter
私は、なぜこの種の実装が以下よりも優れているのかを理解しようとしています。
myObject = InstanceFromComponentThatWouldProvideTheService();
myObject.DoSomething();
サービスの方法を使用してインターフェイスを取得する場合、実際には、サービスを提供するコンポーネントのインスタンスを取得するだけです。正しい?インターフェイス「インスタンス」を持つことはできません。そして、サービスのプロバイダーになることができるクラスは1つだけです。つまり、実際に持っているのはコンポーネントクラスのインスタンスだけですが、唯一の違いは、コンポーネントオブジェクトのサブセット(インターフェイスにあるサブセット)にしかアクセスできないことです。
これは、パブリックメソッドとプライベートメソッドおよびプロパティを持っているだけとどう違うのですか?言い換えれば、コンポーネントのパブリックメソッド/プロパティis "interface"であり、この回りくどいことで停止できます。何も壊さずにその「インターフェース」の実装方法を変更することはできます(メソッドのシグネチャを変更するまでは、サービスの実装も壊してしまいます)。
とにかく、コンポーネントとサービスの間には1対1の関係があり(複数のクラスがサービスのプロバイダーとして登録できません)、クラスがのプロバイダーであることがわかりません。複数のサービス(srpなど)。
ですから、この種のフレームワークがどのような問題を解決するのかを理解しようとしていると思います。何が足りないのですか?
XNA自体の例で説明させてください。
ContentManager
コンストラクターはIServiceProvider
を取ります。次に、そのIServiceProvider
を使用してIGraphicsDeviceService
を取得し、次にそれを使用してGraphicsDevice
を取得し、そこにテクスチャやエフェクトなどをロードします。
Game
を取ることはできません-そのクラスは完全にオプションであるため(そして依存アセンブリ内にあるため)。 GraphicsDeviceManager
(IGraphicsDeviceService
の一般的に使用される実装)は、Game
のようにGraphicsDevice
を設定するためのオプションのヘルパークラスであるため、取ることができません。
GraphicsDevice
が作成される前にContentManager
を作成している可能性があるため、GraphicsDevice
を直接取得することはできません(これは正確にデフォルトのGame
クラスが行うこと)。したがって、後でからグラフィックデバイスを取得できるサービスが必要です。
これが本当のキッカーです:それcouldIGraphicsDeviceService
を取り、それを直接使用します。 [〜#〜] but [〜#〜]:将来のある時点でXNAチームが(たとえば)AudioDevice
一部のコンテンツタイプが依存するクラス?次に、ContentManager
コンストラクターのメソッドシグネチャを変更してIAudioDeviceService
などを取得する必要があります。これにより、サードパーティのコードが破損します。サービスプロバイダーを持つことで、この問題を回避できます。
実際、XNAチームが共通のリソースを必要とする新しいコンテンツタイプを追加するのを待つ必要はありません。カスタムのContentTypeReader
を作成すると、コンテンツマネージャーからIServiceProvider
にアクセスできます。 好きなサービス-あなた自身のものでも!このようにして、カスタムコンテンツタイプは、XNAコードがそれらや必要なサービスについて知る必要なしに、ファーストクラスのXNAグラフィックスタイプが使用するのと同じメカニズムを使用できます。
(逆に、ContentManager
でグラフィックスタイプをロードしない場合は、グラフィックスデバイスサービスを提供する必要はありません。)
もちろん、これはXNAのようなlibraryにとってはすべてうまく機能します。XNAは3番目に壊れることなく更新可能である必要があります-パーティーコード。特に、サードパーティによって拡張可能なContentManager
のようなものの場合。
ただし:DrawableGameComponent
を使用して走り回っている人がたくさんいて、共有のSpriteBatch
を簡単に取得できないことがわかったので、ある種のスプライトを作成しました-それを渡すためのバッチサービス。これは、一般的にバージョン管理、アセンブリ依存性、またはサードパーティの拡張性の要件がないゲームに必要なものよりもはるかに複雑です。 Game.Services
存在します、それを使用しなければならないという意味ではありません! canが(SpriteBatch
インスタンスのような)ものを直接渡すことができる場合-それを行うだけです-それははるかに単純でより明白です。
その背後にあるアーキテクチャの原則に関する良いスタートについては、 http://en.wikipedia.org/wiki/Dependency_inversion_principle (およびそのリンク)を参照してください。
サービスプロバイダーを使用することは、コードのどの部分がコードの他の特定の部分にアクセスできるかをより適切に制御する方法でもあります。コードを介してオブジェクトを渡すのと同様に、コードを介してIServiceProvider実装を特定のモジュールに渡すことができます。これにより、これらのモジュールは、サービスプロバイダーを通じてアクセス可能な特定のサービスにアクセスできるようになります。
多くのクラスにIServiceProviderインターフェイスを実装させることができ、各クラスは1つ以上のサービスへのアクセスを提供できます。それらは単一のインスタンスを返すことに制限されません(それが自分自身または別のオブジェクトにあるかどうかに関係なく)。
たとえば、キーボード処理、マウス処理、およびAIアルゴリズムのサービスを含むIServiceProviderを使用することができます。このインターフェースをコード内のさまざまなモジュールまたはマネージャーに渡すと、それらのモジュールまたはマネージャーは、必要なサービス(AIサービスへのアクセスが必要なEnemyManagerなど)を取得できます。