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?
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.
See http://en.wikipedia.org/wiki/Dependency_inversion_principle (and it's links) for a good start as to the architectural principles behind it
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With