web-dev-qa-db-ja.com

なぜサービス(IServiceProvider)を使用するのですか?

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など)。

ですから、この種のフレームワークがどのような問題を解決するのかを理解しようとしていると思います。何が足りないのですか?

17
idlewire

XNA自体の例で説明させてください。

ContentManagerコンストラクターはIServiceProviderを取ります。次に、そのIServiceProviderを使用してIGraphicsDeviceServiceを取得し、次にそれを使用してGraphicsDeviceを取得し、そこにテクスチャやエフェクトなどをロードします。

Gameを取ることはできません-そのクラスは完全にオプションであるため(そして依存アセンブリ内にあるため)。 GraphicsDeviceManagerIGraphicsDeviceServiceの一般的に使用される実装)は、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インスタンスのような)ものを直接渡すことができる場合-それを行うだけです-それははるかに単純でより明白です。

17
Andrew Russell

その背後にあるアーキテクチャの原則に関する良いスタートについては、 http://en.wikipedia.org/wiki/Dependency_inversion_principle (およびそのリンク)を参照してください。

6
Eddy

インターフェースはより明確で簡単です モック

ユニットテスト ポリシーによっては、これが重要になる場合があります。

1

サービスプロバイダーを使用することは、コードのどの部分がコードの他の特定の部分にアクセスできるかをより適切に制御する方法でもあります。コードを介してオブジェクトを渡すのと同様に、コードを介してIServiceProvider実装を特定のモジュールに渡すことができます。これにより、これらのモジュールは、サービスプロバイダーを通じてアクセス可能な特定のサービスにアクセスできるようになります。

多くのクラスにIServiceProviderインターフェイスを実装させることができ、各クラスは1つ以上のサービスへのアクセスを提供できます。それらは単一のインスタンスを返すことに制限されません(それが自分自身または別のオブジェクトにあるかどうかに関係なく)。

たとえば、キーボード処理、マウス処理、およびAIアルゴリズムのサービスを含むIServiceProviderを使用することができます。このインターフェースをコード内のさまざまなモジュールまたはマネージャーに渡すと、それらのモジュールまたはマネージャーは、必要なサービス(AIサービスへのアクセスが必要なEnemyManagerなど)を取得できます。

0
Samuel Slade