用I前缀接口?

时间:2011-04-28 10:09:23

标签: unit-testing interface dependency-injection tdd coding-style

我正在阅读Rober Martin(UncleBob)的“清洁代码”,并且一般都喜欢UncleBob的思考。但是,当我读到他避免使用像“IPerson”这样的接口前缀时,我有点困惑。他说:“我不希望我的用户知道我正在为他们提供一个界面”。

从TDD /注入角度思考,我总是非常有兴趣告诉我正在处理接口的类的“用户”。主要原因是我认为接口在系统的不同“代理”之间收缩。在我的系统的一个角落工作的代理,不应该知道另一个代理工作的具体实现;他们只应该交换合同,并期望在不知道如何履行合同的情况下履行合同。另一个但也非常重要的原因是可以完全模拟界面,从而使单元测试更容易。你可以在具体课上嘲笑多少是有限的。

因此,我更倾向于想象我确实在交接接口......或者以接口作为参数。但是因为UncleBob是我们社区中的重量级冠军,而我只是另一位flyweigth桌面骑师,我想知道我是否遗漏了一些东西。

我坚持使用接口是不对的?

3 个答案:

答案 0 :(得分:56)

我们已经习惯了Java和C#中的一些约定;但这是倒退的。例如,从技术角度来看,将私有变量放在每个类顶部的惯例是非常愚蠢的。关于班级最重要的事情是它的公共方法。 最少重要的事情,我们<隐藏隐私障碍背后的事物,就是实例变量。那么我们为什么要把它们放在最顶端呢?

接口前面的“I”是另一种向后的惯例。当您传递对对象的引用时,您应该期望它是一个接口。接口应该是默认的;所以没有必要做一些额外的事情,比如使用我的前缀,宣布你正在做每个人都希望你做的事情。如果我们为传递具体类的异常条件保留一个特殊标记会更好(尽管仍然是错误的)。

使用I的另一个问题是(奇怪地)我们使用它来传递使用接口的实现决策。通常我们不希望实施决策如此大声地表达,因为这使得它们难以改变。例如,考虑一下,如果您认为IFoo确实应该是抽象类而不是接口,可能会发生什么。您应该将名称更改为Foo或CFoo,还是ACFoo?

我能听到车轮转过头来。你在想:“是的,但界面在语言中占有特殊的位置,因此用特殊的命名约定标记它们是合理的。”确实如此。但是整数在语言中也有一个特殊的位置,我们不再标记它们(不再是)。除此之外,问问自己,为什么接口在语言中有特殊的位置?

Java和C#中的接口背后的整个想法是一个警察。语言设计者可能刚刚使用了抽象类,但他们担心实现多重继承的困难。所以他们与自己做了一个后台交易。他们发明了一种人工构造(即接口),它将提供一些多重继承的能力,并且它们将正常类限制为单一继承。

这是语言设计师做出的最糟糕的决定之一。他们发明了一种新的重量级语法元素,以便排除一个有用且强大(尽管有争议)的语言特性。接口不是为了启用而发明的,它们被发明为禁用。界面是设计师用语言进行的黑客攻击,他们不想解决更难的MI问题。因此,当您使用I前缀时,您将成为语言历史上最大的黑客之一的焦点。

下次你写这样的函数签名时:

public void myFunction(IFoo foo) {...}

问问自己:“为什么我想知道IFoo的作者使用'界面'这个词?对我来说,无论他使用'界面'还是'阶级'还是'结构',它有什么区别?< em>那是他的事,不是我的事!那么他为什么要把这个伟大的我放在他的类型名称面前强迫我了解他的事业呢?他为什么不把他的声明压缩起来并保留他的私人权利我的脸?“

答案 1 :(得分:9)

如果你在讨论.NET,那么开头的I接口就会无处不在,丢掉它们会让每个人都感到困惑。

另外,我更愿意拥有

public class Foo : IFoo {}

大于

public class FooImpl : Foo {}

这一切都归结为个人偏好,我自己做了一段时间玩这个想法,但我回到了I前缀。 YMMV

答案 2 :(得分:7)

  

我认为接口合同   在不同的“代理人”之间   系统。与之合作的代理人   我系统的一角,应该不知道   另一个具体实施   代理人工作;他们应该只交换   合同,并期望合同   不知道如何实现。该   其他,但也很重要的原因   是一个界面可以嘲笑   完全,从而进行单元测试   更容易。如何限制   你可以嘲笑一个具体的课程。

所有这一切都是正确的 - 但它如何需要接口的命名约定?

基本上,为接口加上“I”前缀只是匈牙利符号无用的另一个例子,因为在静态类型语言中(唯一一种接口作为语言结构有意义),你总能轻松快速地找到它输出什么类型,通常是在IDE中将鼠标悬停在它上面。