TDD - 提取接口或使方法虚拟

问题描述 投票:0回答:3

每当我想在一个微不足道的类中存根一个方法时,我通常会提取一个接口。 现在,如果该类的构造函数是公共的,并且不太复杂或依赖于复杂类型,那么将相关方法设为虚拟并继承将具有相同的效果。 这比提取接口更好吗?如果是这样,为什么?

编辑:

class Parser
{
    public IDictionary<string, int> DoLengthyParseTask(Stream s)
    {
        // is slow even with using memory stream
    }
}

有两种方法:要么提取接口,要么将方法虚拟化。我实际上更喜欢接口,但这可能会导致

IParser
Parser
元组的爆炸......

c# tdd
3个回答
5
投票

您需要考虑在单元测试之外您想要完成什么。


4
投票

接口只是为你签订一份合同,基本上是一个承诺,即你的实现将提供对一组指定的接触点(方法、属性等)的访问,而没有行为规范。只要你遵守承诺,你可以做任何你想做的事。

另一方面,除了契约之外,基类至少指定了类中编码的一些行为(除非一切都是抽象的,但那是另一个故事)。将方法设为虚拟仍然使您能够调用基础的实现,并且仍然提供您自己的代码。

这种行为继承基本上就是为什么多重继承在现代 OOP 中是禁忌的原因,并且多重接口实现相对常见。

也就是说,你需要权衡你是只想提取合同,还是还想提取某些行为,并且对于特定情况,答案应该是显而易见的。

至于

IParser
/
Parser
对,首先它们非常适合单元测试和依赖项注入,其次,它们不会向您收取类创建费用,因此您可以随意创建任意数量的类。


4
投票

通过对接口进行编程,您可以在单元测试和松散耦合的代码中轻松进行模拟/存根(因此,灵活性更高),实际上是免费(唯一的缺点是需要管理更多工件) .

接口和继承是两个独立的事物,互换使用它们并不是一个好主意,尽管这是可能的。通过标记方法

virtual
,您实际上是在告诉其他人,他们不仅可以在其实现中自由更改(覆盖)此方法,而且您实际上期望他们(您是吗?)。

这样的设计会带来相当严重的后果,所以除非你明确需要它 - 你不应该使用它。尝试坚持使用接口编程。

良好的面向对象设计原则之一指出,您应该对接口进行编程(按契约设计里氏替换原则)并且更喜欢组合而不是继承(不仅您的类应该实现接口/抽象类,而且还应该实现由此类实现组成)。

值得注意的是,您的

Parser
示例非常适合隐藏在抽象后面(无论是接口还是基类)。从消费者的角度来看,如何创建数据并不重要 - 现在您可能认为它只是 XML 流,但需求往往会发生变化(和/或增长),并且您可能很快就会发现自己正在实现二进制文件解析器,数据流挖掘解析器等等。现在就正确做,为自己节省时间和以后的麻烦。

© www.soinside.com 2019 - 2024. All rights reserved.