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

发布于 2024-11-26 01:38:55 字数 786 浏览 0 评论 0原文

我是在探索 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();

当您使用服务方式获取接口时,您实际上只是得到一个无论如何提供服务的组件的实例。正确的?您不能拥有接口“实例”。并且只有一个类可以成为服务的提供者。因此,您真正拥有的只是组件类的一个实例,唯一的区别是您只能访问组件对象的子集(无论接口中的子集是什么)。

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

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

所以我想我正在试图弄清楚这种框架要解决什么问题。我缺少什么?

I'm coming to this question from exploring the XNA framework, but I'd like a general understanding.

ISomeService someService = (ISomeService)Game.GetServices(typeof(ISomeService));

and then we do something with whatever functions/properties are in the interface:

someService.DoSomething();  // let's say not a static method but doesn't matter

I'm trying to figure out why this kind of implementation is any better than:

myObject = InstanceFromComponentThatWouldProvideTheService();

myObject.DoSomething();

When you use the services way to get your interface, you're really just getting an instance of the component that provides the service anyway. Right? You can't have an interface "instance". And there's only one class that can be the provider of a service. So all you really have is an instance of your component class, with the only difference being that you only have access to a subset of the component object (whatever subset is in the interface).

How is this any different from just having public and private methods and properties? In other words, the public methods/properties of the component is the "interface", and we can stop with all this roundaboutness. You can still change how you implement that "interface" without breaking anything (until you change the method signature, but that would break the services implementation too).

And there is going to be a 1-to-1 relationship between the component and the service anyway (more than one class can't register to be a provider of the service), and I can't see a class being a provider of more than one service (srp and all that).

So I guess I'm trying to figure out what problem this kind of framework is meant to solve. What am I missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绝不放开 2024-12-03 01:38:55

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

ContentManager 构造函数采用 IServiceProvider。然后,它使用该 IServiceProvider 来获取 IGraphicsDeviceService,进而使用该 IGraphicsDeviceService 来获取一个 GraphicsDevice,在该设备上加载纹理、效果等内容。等等。

它不能采用 Game - 因为该类完全是可选的(并且位于依赖程序集中)。它不能采用 GraphicsDeviceManagerIGraphicsDeviceService 的常用实现),因为像 Game 一样,它是一个用于设置 >图形设备

它不能直接获取 GraphicsDevice,因为您可能在创建 GraphicsDevice 之前创建 ContentManager(这完全< /em> 默认 Game 类的作用)。因此,它需要一个可以稍后从检索图形设备的服务。

现在真正的问题是:可以采用IGraphicsDeviceService并直接使用它。 但是:如果 XNA 团队在将来的某个时候添加(例如)某些内容类型所依赖的 AudioDevice 类怎么办?然后,您必须修改 ContentManager 构造函数的方法签名以获取 IAudioDeviceService 或其他内容 - 这会破坏第三方代码。通过拥有服务提供商,您可以避免这个问题。

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

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

当然,这对于 < em>library如 XNA,需要在不破坏第三方代码的情况下进行更新。特别是对于像 ContentManager 这样可由第三方扩展的东西。

但是:我看到很多人使用 DrawableGameComponent,发现无法轻松地将共享 SpriteBatch 放入其中,因此创建某种精灵批量服务来传递它。对于通常不需要担心版本控制、程序集依赖性或第三方可扩展性要求的游戏来说,这比您需要的要复杂得多。仅仅因为 Game.Services 存在,并不意味着您必须使用它!如果您可以直接传递事物(例如SpriteBatch实例) - 就这样做 - 它会更简单、更明显。

Allow me to explain it via an example from XNA itself:

The ContentManager constructor takes a IServiceProvider. It then uses that IServiceProvider to get a IGraphicsDeviceService, which it in turn uses to get a GraphicsDevice onto which it loads things like textures, effects, etc.

It cannot take a Game - because that class is entirely optional (and is in a dependent assembly). It cannot take a GraphicsDeviceManager (the commonly used implementation of IGraphicsDeviceService) because that, like Game is an optional helper class for setting up the GraphicsDevice.

It can't take a GraphicsDevice directly, because you may be creating a ContentManager before the GraphicsDevice is created (this is exactly what the default Game class does). So it takes a service that it can retrieve a graphics device from later.

Now here is the real kicker: It could take a IGraphicsDeviceService and use that directly. BUT: what if at some time in the future the XNA team adds (for example) an AudioDevice class that some content types depend on? Then you'd have to modify the method signature of the ContentManager constructor to take an IAudioDeviceService or something - which will break third-party code. By having a service provider you avoid this issue.

In fact - you don't have to wait for the XNA team to add new content types requiring common resources: When you write a custom ContentTypeReader you can get access to the IServiceProvider from the content manager and query it for whatever service you like - even your own! This way your custom content types can use the same mechanism as the first-class XNA graphics types use, without the XNA code having to know about them or the services they require.

(Conversely, if you never load graphics types with your ContentManager, then you never have to provide it with a graphics device service.)

This is, of course, all well and good for a library like XNA, which needs to be updatable without breaking third-party code. Especially for something like ContentManager that is extendible by third parties.

However: I see lots of people running around using DrawableGameComponent, finding that you can't get a shared SpriteBatch into it easily, and so creating some kind of sprite-batch-service to pass that around. This is a lot more complication than you need for a game which generally has no versioning, assembly-dependency, or third-party extensibility requirements to worry about. Just because Game.Services exists, doesn't mean you have to use it! If you can pass things (like a SpriteBatch instance) around directly - just do that - it's much simpler and more obvious.

怀念你的温柔 2024-12-03 01:38:55

请参阅 http://en.wikipedia.org/wiki/Dependency_inversion_principle (及其链接)了解其背后的架构原则是一个良好的开端

See http://en.wikipedia.org/wiki/Dependency_inversion_principle (and it's links) for a good start as to the architectural principles behind it

一身骄傲 2024-12-03 01:38:55

界面更清晰,更容易模拟

这可能很重要,具体取决于您的单元测试策略。

Interfaces are clearer and easier to mock.

That can be important, depending on your unit test policy.

萧瑟寒风 2024-12-03 01:38:55

使用服务提供程序也是更好地控制代码的哪些部分可以访问代码的某些其他部分的一种方法。与通过代码传递对象类似,您可以通过代码将 IServiceProvider 实现传递到特定模块。这将允许这些模块访问可通过服务提供商访问的某些服务。

您可以让许多类实现 IServiceProvider 接口,每个类都可以提供对一个或多个服务的访问 - 它们不限于返回单个实例(无论是对于它们自己还是另一个对象)。

例如,一个用途可能是拥有一个 IServiceProvider,其中包含键盘处理、鼠标处理和 AI 算法的服务。将此接口传递给代码中的不同模块或管理器将允许这些模块或管理器检索它们所需的服务(例如需要访问 AI 服务的 EnemyManager)。

Using a service provider is also a way of better controlling what portions of your code have access to certain other portions of your code. Similarly to passing an object through your code, you can pass an IServiceProvider implementation through the code to specific modules. This would allow for those modules to access certain services that are accessible through the service provider.

You can have many classes implement the IServiceProvider interface, each of which could provide access to one or more services - they are not restricted to returning a single instance (whether that be to themselves or another object).

For example, a use may be to have an IServiceProvider that contains services for keyboard handling, mouse handling and AI algorithms. Passing this interface to different modules or managers within your code will allow those modules or managers to retrieve the services they require (such as an EnemyManager needing access to the AI service).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文