为什么要使用服务(IServiceProvider)?

idl*_*ire 17 c# xna

我是从探索XNA框架来探讨这个问题的,但我想了解一般情况.

ISomeService someService = (ISomeService)Game.GetServices(typeof(ISomeService));
Run Code Online (Sandbox Code Playgroud)

然后我们对界面中的任何函数/属性做一些事情:

someService.DoSomething();  // let's say not a static method but doesn't matter
Run Code Online (Sandbox Code Playgroud)

我试图找出为什么这种实现比以下更好:

myObject = InstanceFromComponentThatWouldProvideTheService();

myObject.DoSomething();
Run Code Online (Sandbox Code Playgroud)

当您使用服务方式获取接口时,您实际上只是获取了提供服务的组件实例.对?你不能有一个接口"实例".而且只有一个类可以成为服务的提供者.所以你真正拥有的只是组件类的一个实例,唯一的区别是你只能访问组件对象的子集(界面中的任何子集).

这有什么不同于公共和私人方法和属性?换句话说,组件的公共方法/属性 "接口",我们可以停止所有这些迂回.您仍然可以更改实现"接口"的方式而不会破坏任何内容(直到您更改方法签名,但这也会破坏服务实现).

无论如何,组件和服务之间将存在一对一的关系(多个类不能注册成为服务的提供者),并且我看不到类是提供者的不止一项服务(srp和所有).

所以我想我正在试图找出这种框架要解决的问题.我错过了什么?

And*_*ell 17

请允许我通过XNA本身的示例来解释它:

ContentManager构造需要IServiceProvider.然后它使用它IServiceProvider来获取a IGraphicsDeviceService,它反过来用来获取GraphicsDevice它加载纹理,效果等内容.

它不能Game- 因为该类完全是可选的(并且在依赖程序集中).它不能采用GraphicsDeviceManager(常用的实现IGraphicsDeviceService),因为它Game是一个可选的辅助类来设置GraphicsDevice.

它不能GraphicsDevice直接使用,因为您可能在创建ContentManager之前创建了一个GraphicsDevice(这正是默认Game类所做的).因此,它需要一项服务,它可以从以后检索图形设备.

现在,这里是真正的踢球者:可以采取IGraphicsDeviceService直接使用.但是:如果在未来的某个时间XNA团队添加(例如)AudioDevice某些内容类型所依赖的类,该怎么办?然后你必须修改ContentManager构造函数的方法签名来取一个IAudioDeviceService或者什么 - 这会破坏第三方代码.通过提供服务提供商可以避免此问题.

实际上 - 您不必等待XNA团队添加需要公共资源的新内容类型:当您编写自定义时,ContentTypeReader您可以访问IServiceProvider内容管理器并查询它以获取您喜欢的任何服务 - 甚至是您自己的服务!这样,您的自定义内容类型可以使用与第一类XNA图形类型相同的机制,而XNA代码无需了解它们或所需的服务.

(相反,如果您从未使用图形类型加载图形类型ContentManager,那么您永远不必为其提供图形设备服务.)

当然,对于像XNA这样的来说,这一切都很好,这需要在不破坏第三方代码的情况下进行更新.特别是对于类似的东西ContentManager,可以由第三方扩展.

但是:我看到很多人在乱用DrawableGameComponent,发现你不能SpriteBatch轻易地分享它,所以创建某种精灵批处理服务来传递它.这比通常没有版本控制,程序集依赖性或第三方可扩展性要求的游戏所需的复杂程度要高得多.仅仅因为Game.Services存在,并不意味着你必须使用它!如果你可以直接传递东西(比如SpriteBatch实例) - 就这样做 - 它更简单,更明显.

  • 那么说服务提供者就像一个将服务映射到类对象的字典会是错误的吗?通过这种方式,它提供了另一个抽象层,调用者可以指定服务名称,而无需知道要实现它的类.但是,调用者需要知道它将能够在该对象上调用哪些方法,因此服务接口......我是对的,这对于完全控制其代码库的程序员/团队来说是有限的用途. ?在某个地方重复使用它有多大用处? (3认同)
  • 正确.`IServiceProvider`基本上是`Type`到`object`的字典.但是`Type`是一个`接口`并且返回的`object`实现了该接口是一种很好的做法.对于您自己的内部重用,它并不是特别有用 - 因为您还可以修改代码(与XNA本身不同,您无法修改).这里的重点(比如您的命令行示例)是您根本不必使用服务架构!你只需要使用`interface`.`IServiceProvider`只是传递接口的一种(相当复杂的)方法. (3认同)
  • @angryInsomniac没有功能或性能劣势.在我的专家看来,这更难维护(特别是随着团队规模或游戏复杂性的增加).例如:每个组件将服务留给下一个组件的状态是什么?如果他们改变秩序怎么办?如果您需要在组件之间进行实际批处理,该怎么办?如果您需要更改绘图模式怎么办?如果将SpriteBatch对象作为参数传递,则更容易"看到"这些内容. (2认同)

Edd*_*ddy 6

请参阅http://en.wikipedia.org/wiki/Dependency_inversion_principle(及其链接),了解其背后的架构原则.