How can an interface be completely independent of an implementation if we must specify method parameters
Pardon me if this is a naive question. An interface
is supposed to hide the details of an implementation, but doesn't it necessarily reveal aspects of that implementation due to what parameters its methods take? The parameters that a method needs inevitably tells us something about how that method is going about fulfilling its function.
This seems especially problematic in the area of Inversion Of Control, where the higher-level component seems to "own" the interface. The way I understand it, the higher-level component basically says: I need some low-level component to fulfill some functions. Here's the functions I need (in the form of an interface), and then another class is free to provide that service.
But in publishing that interface, how does the higher-level component know what parameters the implementor is going to need?
And if the implement开发者_如何学Cor goes and changes the method signatures of the interface, then we now have a strong coupling between interface and implementation which we were trying to avoid.
Is there some abstraction that takes care of this issue? Should we just "over-parameterize" the method signatures in our interfaces, and let the implementor ignore the arguments he doesn't need? Or should we always have no parameters and let the implementor deal with getting what it needs from somewhere else?
UPDATE: Let me try to give a concrete example. Let's say I have a LightController that is implementing an interface with a Detect() method or something like that. I'm a higher-level "Room" component, and I want it to turn on the lights when someone enters the room. What parameters do I define as part of the Detect signature? What if I pass it in a Sound parameter, so that the lights can toggle based on sound level? But heck, there's a class that wants to implement this using movement detection, not sound. And I really don't care one way or another. So do I just have to pass in the entire state of the room and let the implementor decide what it needs?
UPDATE2: Let me be more explicit with code:
interface ILightController {
bool Detect(paramIThinkImplementorWillNeed);
}
class Room {
ILightController lc = GetService(ILightController);
if (lc.Detect(thatParam))
...
}
class MovementLightController : NOTILightController { // nope
public bool Detect(aDifferentParam) {
// i could very well have fulfilled this functionality but the
// interface does not allow for the input i need. that's too bad.
}
}
I think one solution would be if the "contract" went both ways. What if the caller of the ILightController methods had to satisfy another interface that put constraints on it, and then passed itself to every method of the LightController interface, from which light controller's could get the input they need. So:
interface ILightControllerInput {
public PropertyNeededByImplementorsOfILightController { get; set; }
}
interface ILightController {
public Detect(ILightControllerInput input);
public SomeOtherMethod(ILightControllerInput input);
// etc..
}
class Room : ILightControllerInput {
public PropertyNeededByImplementorsOfILightController { get; set; }
ILightController lc = GetService(ILightController);
if (lc.Detect(this)) ...
}
class MovementLightController : ILightController {
public bool Detect(ILightControllerInput input) {
// that's right baby, it's a 2-way street here. if i'm going to conform
// to your interface, you're going to conform to what i need!
}
}
And then from this, you could have a services implementation that goes both ways. The higher-level component can find a lower-level component that implements the interface it needs, but the higher-level component must specify what inputs it is capable of passing, and that will match a subset of the available low-level components (so the "needs-fulfillment" goes both ways).
Please tell me, am I smoking crack? Or does something like this already exist (as a pattern, etc.)?
Yes, it does necessarily reveal some hints. Necessary is pretty key here. If I'm making an interface for something that's meant to take images and load/save them from somewhere, at the very least I need to know what image to load and what data to save (and what key to use so I can load it again later).
How would you do that without defining what the key looks like, and what format the data to save is in? An interface without that definition is useless because without it, two implementers are free to come up with two different ways of doing it. Once you do that, I can't swap one implementation for another one and it's no longer really an interface.
"Hiding all the details of implementation" is an ideal. In reality we don't always get there. :)
The parameters are considered part of the interface. They in fact don't tell you anything about the implementation.
For example if you want to say convert a string to lower case. Of course you would pass the string to convert as parameter, right. It's part of the interface.
Yes, interfaces are supposed to decouple and hide the implementation. However, they also define a contract, that is: input and output. For it to work, one must know what is the expected input and what the expected output. The implementation-hiding is what is between that, the transformation of input into output.
You should carefully decide what interfaces you're going to abstract to. It's all about making the right design decisions when creating your system. An interface
in C# or .NET for that matter, is defined by the methods it exposes. These methods have signatures, being their return values and the parameters they take, the name of the method is also of importance.
This defines your interface and tells the clients using this interface what this is going to be doing, if the interface is well defined. How it will be doing it, is left to implementation and is not important.
You cannot decouple anymore then this, else what are you going to have left to define? There has to be some kind of entry point to your communication between your clients and your code. The interface is that communication point. This tells your clients what it's going to do, and that's all it tells, not how, just what
If an implementation needs some implementation-specific aspects, those should be passed in during initialization and probably should be in the constructor. That's what IoC is all about, you don't need to know the details, only the important parts.
For example, imagine you have IWageProcessor
that has a method decimal GetWage(Employee employee)
. Nothing in that method is implementation specific. If the implementation of IWageProcessor
needs something else (like a database connection), IoC will take care of that. If you want to change the implementation of IWageProcessor
so that it doesn't use database, but WCF, you can do so and the interface won't change.
That doesn't mean that the interface can never change. But if it changes, it's only when the meaning of the methods changes, not just the implementation. For example, if it's decided that getting the current wage is not enough and we need to get the wage based on month, the method can be changed to decimal GetWage(Employee employee, Month month)
. Doing so will of course change the implementation, but it's not the other way around. If the implementation changed, but not the meaning, the interface would stay the same.
精彩评论