是否每个对象都有一个接口,所有对象都松散耦合?

时间:2008-12-09 12:52:15

标签: oop language-agnostic interface

从我所读到的最佳实践是基于接口的类和松散地耦合对象,以帮助代码重用和单元测试。

这是正确的,是否应该始终遵循规则?

我问的原因是我最近在一个系统中使用了100个非常不同的对象。一些共享的通用接口,但大多数都没有,并想知道它是否应该有一个接口镜像这些类中的每个属性和函数?

我正在使用C#和dot net 2.0但是我相信这个问题适合多种语言。

7 个答案:

答案 0 :(得分:52)

它对真正提供服务的对象很有用 - 身份验证,存储等。对于没有任何进一步依赖的简单类型,以及永远不会有任何替代实现的情况,我认为可以使用具体的类型。

如果你过度使用这种东西,你最终会花费很多时间来嘲笑/抄袭世界上的所有东西 - 这通常会导致脆弱的测试。

答案 1 :(得分:13)

不是真的。服务组件(为您的应用程序执行操作的类)非常适合接口,但作为一项规则,我不打算为基本实体类提供接口。

例如: 如果您正在处理域模型,那么该模型不应该是接口。但是,如果该域模型想要调用服务类(如数据访问,操作系统函数等),那么您应该查看这些组件的接口。这减少了类之间的耦合,意味着它是接口或耦合的“契约”。

在这种情况下,您开始发现编写单元测试更容易(因为您可以使用存根/模拟/伪造数据库访问等)并且可以使用IoC交换组件而无需重新编译应用程序。

答案 2 :(得分:8)

我只使用需要这种抽象级别的接口 - 即你需要使用多态行为。常见的例子是依赖注入,或者某个地方有工厂类型的场景,或者你需要建立一个“多重继承”类型的行为。

就我而言,凭借我的开发风格,这种情况经常发生(我赞成在UI控件以外的大多数事情上使用深度继承层次结构进行聚合),但我看到使用非常少的非常好的应用程序。这一切都取决于......

哦,是的,如果您大量使用接口 - 请注意Web服务。如果需要通过Web服务公开对象方法,则它们无法真正返回或接受接口类型,只能使用具体类型(除非您要手动编写所有自己的序列化/反序列化)。是的,这让我感到非常痛苦......

答案 3 :(得分:5)

界面的缺点是它们无法进行版本控制。一旦您发布了界面,您将不会对其进行更改。如果您使用抽象类,那么您可以通过添加新方法并将它们标记为虚拟来轻松扩展合同。

例如,.NET中的所有流对象都派生自System.IO.Stream,它是一个抽象类。这使Microsoft可以轻松添加新功能。在frameworkj的第2版中,他们添加了 ReadTimeout WriteTimeout 属性,而不会破坏任何代码。如果他们使用了一个接口(比如IStream),那么他们就无法做到这一点。相反,他们必须创建一个新接口来定义超时方法,如果我们想要使用该功能,我们必须编写代码以有条件地转换为此接口。

答案 4 :(得分:3)

当您想要清楚地定义软件的两个不同部分之间的交互时,应该使用接口。特别是当您可能想要撕掉连接的任何一端并用其他东西替换它时。

例如,在我的CAM应用程序中,我将CuttingPath连接到Points of Points。拥有一个IPointList接口是没有意义的,因为CuttingPaths总是由我的应用程序中的Points组成。

然而,我使用接口IMotionController与机器通信,因为我们支持许多不同类型的切割机,每个切割机都有自己的推荐集和通信方法。因此,在这种情况下,将它放在接口后面是有意义的,因为一个安装可能使用不同的机器。

我们的应用程序自80年代中期以来一直保持着,并在90年代后期进行了面向对象的设计。我发现可能发生的变化大大超出了我原先的想法,界面的使用也在增长。例如,过去我们的DrawingPath由点组成。但现在它由实体(样条线,弧,ec)组成,因此它指向一个EntityList,它是实现IEntity接口的Object的集合。

但是,这种变化是由于认识到可以使用许多不同的方法绘制DrawingPath而推动的。一旦认识到需要各种绘图方法,就表明需要一个接口而不是与实体对象的固定关系。

请注意,在我们的系统中,DrawingPaths会渲染到低级切割路径,这些切割路径始终是一系列点段。

答案 5 :(得分:2)

我试图在最近的一个项目中从字面上接受“代码到接口”的建议。最终结果基本上是在接口(大I)实现中复制每个类的公共接口(小i)。这在实践中毫无意义。

我觉得更好的策略是将界面实现限制在动词

Print()
Draw()
Save()
Serialize()
Update()

...等等。这意味着主要作用是存储数据的类 - 如果您的代码设计得很好,通常只会这样做 - 不需要或不需要接口实现。您可能希望在任何地方使用运行时可配置的行为,例如表示相同数据的各种不同图形样式。

当要求工作的东西真的不想知道工作是如何完成时,情况会更好。这意味着你可以给它一个macguffin,它可以简单地信任它会做它的公共界面所做的任何事情,并且让有问题的组件只需选择 来完成工作。

答案 6 :(得分:1)

我同意kpollock。接口用于获得对象的共同点。它们可用于IOC容器和其他用途的事实是一个附加功能。

假设您有几种类型的客户类略有不同但具有共同属性。在这种情况下,有一个ICustomer接口将它们绑定在一起是很好的,逻辑上。通过这样做,您可以创建一个CustomerHander类/方法,以相同的方式处理ICustomer对象,而不是为每个客户变体创建一个handerl方法。

这是接口的优势。 如果你只有一个实现接口的类,那么接口对它没有多大帮助,它只是坐在那里什么都不做。