Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use services (IServiceProvider)?

Tags:

c#

xna

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?

like image 725
idlewire Avatar asked Jul 23 '11 17:07

idlewire


2 Answers

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.

like image 197
Andrew Russell Avatar answered Oct 02 '22 19:10

Andrew Russell


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

like image 22
Eddy Avatar answered Oct 02 '22 20:10

Eddy