Why use services (IServiceProvider)?

前端 未结 4 931
执念已碎
执念已碎 2020-12-13 20:42

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

ISomeService someService = (ISomeService)Game.GetServic         


        
相关标签:
4条回答
  • 2020-12-13 21:21

    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.

    0 讨论(0)
  • 2020-12-13 21:35

    Interfaces are clearer and easier to mock.

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

    0 讨论(0)
  • 2020-12-13 21:39

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

    0 讨论(0)
  • 2020-12-13 21:46

    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).

    0 讨论(0)
提交回复
热议问题