2008-12-09 75 views
38

从我读过的最佳实践中可以看出,基于接口的类和松散耦合的对象有助于代码的重用和单元测试。每一个对象都应该有一个接口,并且所有对象都是松散耦合的?

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

我问的原因是我最近在一个有100个非常不同的对象的系统上工作。几个共享的通用接口,但大多数不接受,并怀疑它是否应该有一个接口镜像这些类中的每个属性和功能?

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

+0

正确的,你应该。左边,你应该有一个发电机为你做“样板”。 – Pacerier 2012-03-06 05:21:15

回答

52

对于真正提供服务的对象(认证,存储等)非常有用。对于没有任何进一步依赖性的简单类型,以及从未有任何替代实现的地方,我认为可以使用具体类型。

如果你遇到这样的事情,你最终会花费很多时间嘲弄/扼杀世界上的所有东西 - 这往往会导致脆弱的测试。

13

不是。服务组件(为你的应用程序做事的类)非常适合用于接口,但是作为一个规则,我不会为诸如基本实体类的接口打扰。

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

在这种情况下,你开始发现编写单元测试要容易得多(因为你可以有数据库访问等的存根/嘲笑/假货等),并且可以使用IoC来交换组件,而无需重新编译应用程序。

8

我只会使用需要抽象级别的接口 - 即您需要使用多态行为。常见的例子是依赖注入,或者你有一个工厂类型的场景,或者你需要建立一个“多重继承”类型的行为。在我的情况下,以我的开发风格,这是非常经常的(我喜欢聚合深度继承层次结构,除了UI控件以外的大多数事情),但我已经看到使用非常少的非常好的应用程序。这一切都取决于...如果你确实大量使用接口,请注意Web服务。如果您需要通过Web服务公开您的对象方法,那么它们不能真正返回或采用接口类型,只能使用具体类型(除非要手动编写所有自己的序列化/反序列化)。是的,这已经让我大伤脑筋......

3

当你想明确定义软件两个不同部分之间的交互时,应该使用接口。尤其是当您想要拆分连接的任一端并将其替换为其他端口时。

例如在我的CAM应用程序中,我有一个连接到Points集合的CuttingPath。由于在我的应用程序中,CuttingPaths总是由Points组成,所以拥有IPointList接口是没有意义的。

但是我使用接口IMotionController与机器进行通信,因为我们支持许多不同类型的切割机,每个切割机都有自己的指令集和通信方法。所以在这种情况下,将其放在界面后面是有道理的,因为一个安装可能使用另一个不同的机器。

我们的应用程序自80年代中期以来一直保持并于90年代后期转向面向对象设计。我发现可能会发生什么变化大大超过了我原先认为的和接口的使用增长。例如,它曾经是我们的DrawingPath由点构成的。但现在它由实体(样条曲线,弧,ec)组成,因此它指向一个EntityList,它是实现IEntity接口的Object的集合。

但是,这种变化是通过认识到DrawingPath可以使用许多不同的方法来绘制的。一旦意识到需要多种绘图方法,那么就需要一个接口而不是与一个实体对象的固定关系。

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

1

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

比方说,您有几种类型的客户类略有差异,但具有共同属性。在这种情况下,拥有一个ICustomer接口将它们绑定在一起非常棒,logicaly。通过这样做,您可以创建类似于ICustomer对象的类/方法,而不是为每个客户变体创建一个handerl方法。

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

5

接口的缺点是它们不能被版本化。一旦您发布界面,您将不会对其进行更改。如果您使用抽象类,那么您可以通过添加新方法并将它们标记为虚拟来轻松地延长合同。

作为一个例子,.NET中的所有流对象都是从System.IO.Stream派生的,它是一个抽象类。这使得微软很容易添加新功能。在frameworkj的第2版中,他们在不破坏任何代码的情况下添加了ReadTimeoutWriteTimeout属性。如果他们使用了一个接口(比如IStream),那么他们将无法做到这一点。相反,他们不得不创建一个新的界面来定义超时方法,如果我们想使用这个功能,我们必须编写代码来有条件地投射到这个界面。

+1

接口继承是允许的。这不会完成同样的事情吗? – 2012-03-06 08:11:58

+0

你说得很好,肖恩,但我有和汤姆一样的问题。如果他们发布了“IStream2:IStream”,会发生什么?一团糟?你能详细说明吗? – toddmo 2015-03-14 23:06:17

2

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

一个更好的策略,我觉得是你的限制接口实现对动词

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

...等等等等,这意味着类,它们的主要作用是存储数据 - 如果你的代码精心设计的他们通常只会这样做 - 不想或不需要接口实现。任何你可能想要的运行时配置行为,例如代表相同数据的各种不同的图形样式。

当事情要求工作真的不想知道工作是如何完成的时候更好。这意味着你可以给它一个macguffin,它可以简单地信任它会做任何公共接口说它做,并让有问题的组件简单地选择做这项工作。

相关问题