如果这是一个天真的问题,请原谅我。 interface
应该隐藏实现的细节,但由于其方法采用什么参数,它是否必然会揭示该实现的各个方面?方法所需的参数不可避免地告诉我们该方法如何实现其功能。
这在控制反转领域似乎特别成问题,其中较高级别的组件似乎“拥有”界面。我理解它的方式,更高级别的组件基本上说:我需要一些低级组件来完成一些功能。这是我需要的功能(以接口的形式),然后另一个类可以免费提供该服务。
但是在发布该接口时,更高级别的组件如何知道实现者需要哪些参数?
如果实现者改变了接口的方法签名,那么我们现在在接口和实现之间有一个强大的耦合,我们试图避免这种情况。
是否有一些抽象可以解决这个问题?我们应该只是“过度参数化”接口中的方法签名,让实现者忽略他不需要的参数吗?或者我们是否应始终拥有 no 参数并让实现者处理从其他地方获取所需内容?
更新:让我试着举一个具体的例子。假设我有一个LightController,它实现了一个带有Detect()方法的接口或类似的东西。我是一个更高级别的“房间”组件,我想让它在有人进入房间时打开灯。我将哪些参数定义为Detect签名的一部分?如果我在Sound参数中传递它,以便灯光可以根据声级进行切换怎么办?但是,有一个班级想要使用运动检测来实现这一点,而不是声音。我真的不在乎这种或那种方式。那么我是否只需要传递整个房间状态并让实现者决定它需要什么?
UPDATE2:让我对代码更加明确:
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.
}
}
我认为一个解决方案就是“合同”是双向的。如果ILightController方法的调用者必须满足另一个对 it 施加约束的接口,然后将其自身传递给LightController接口的每个方法,光控制器可以从中获得所需的输入,该怎么办?所以:
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!
}
}
然后从这里开始,您可以实现两种方式的服务实现。更高级别的组件可以找到实现所需接口的低级组件,但是更高级别的组件必须指定它能够传递的输入,并且这将匹配可用的低级组件的子集(所以“需求实现”是双向的。)
请告诉我,我吸烟了吗?或者这样的事情是否已经存在(作为模式等)?
答案 0 :(得分:2)
是的,它确实显示出一些提示。必要的是这里的关键。如果我正在为某些意图拍摄图像并从某处加载/保存它们的界面创建一个接口,至少我需要知道要加载什么图像以及要保存哪些数据(以及使用哪个键以便我可以加载它再来一次)。
如果不定义键的外观,以及要保存的数据的格式,您将如何做到这一点?没有这个定义的接口是没用的,因为没有它,两个实现者可以自由地提出两种不同的方法。一旦你这样做,我就不能将一个实现换成另一个实现,它不再是一个真正的接口。
“隐藏所有实施细节”是一个理想的选择。实际上,我们并不总能到达那里。 :)
答案 1 :(得分:1)
参数被视为界面的一部分。事实上,他们并没有告诉你有关实施的任何信息。
例如,如果您想将字符串转换为小写。当然你会传递字符串转换为参数,对。它是界面的一部分。
答案 2 :(得分:1)
是的,接口应该解耦并隐藏实现。但是,他们还定义了一个合同,即:输入和输出。要使它工作,必须知道预期输入是什么以及预期输出是什么。实现隐藏就是将输入转换为输出之间的内容。
答案 3 :(得分:1)
您应该仔细确定要抽象的接口。这就是在创建系统时做出正确的设计决策。 C#或.NET中的interface
就此而言,由它公开的方法定义。这些方法具有签名,即它们的返回值和它们所采用的参数,方法的名称也很重要。
这定义了您的接口,并告诉使用此接口的客户端,如果接口定义良好,将会执行此操作。 如何它将继续执行,并不重要。
你不能再解耦了,否则你还要留下什么来定义?您的客户和代码之间的沟通必须有某种切入点。接口就是那个通信点。这告诉客户它将要做什么,这就是它所说的,而不是如何,只是什么
答案 4 :(得分:0)
如果实现需要某些特定于实现的方面,那些应该在初始化期间传入,并且可能应该在构造函数中。这就是IoC的全部意义,你不需要知道细节,只知道重要的部分。
例如,假设您IWageProcessor
有一个方法decimal GetWage(Employee employee)
。该方法中没有任何内容是特定于实现的。如果IWageProcessor
的实现需要其他东西(比如数据库连接),IoC会处理这个问题。如果你想改变IWageProcessor
的实现,以便它不使用数据库,而是使用WCF,你可以这样做,界面不会改变。
这并不意味着界面永远不会改变。但是,如果它发生变化,那只有在方法的含义发生变化时才会发生变化,而不仅仅是实施。例如,如果确定获得当前工资是不够的并且我们需要根据月份获得工资,则该方法可以更改为decimal GetWage(Employee employee, Month month)
。这样做当然会改变实现方式,但事实并非相反。如果实现发生了变化,但没有意义,那么界面将保持不变。