提取接口或使方法虚拟
本文关键字:方法 虚拟 取接口 提取 | 更新日期: 2023-09-27 17:51:13
每当我想在一个无关紧要的类中存根一个方法时,我通常提取一个接口。现在,如果该类的构造函数是公共的,并且不是太复杂或依赖于复杂类型,那么将所讨论的方法设置为虚方法并继承也会产生相同的效果。这比提取接口更可取吗?如果是,为什么?
编辑:class Parser
{
public IDictionary<string, int> DoLengthyParseTask(Stream s)
{
// is slow even with using memory stream
}
}
有两种方法:要么提取一个接口,要么使该方法成为虚方法。实际上我更喜欢接口,但这可能会导致IParser
- Parser
元组的爆炸…
您需要考虑您试图在单元测试之外完成什么。不要让你的工具支配你的设计。
处理接口可以帮助解耦代码,但这些应该是代码中自然的分离点(例如业务逻辑或数据访问)。如果你要继承和覆盖那些方法,使方法虚化是有意义的。
在您的情况下,我会尝试测试行为使用DoLengthyParseTask
而不是直接使用方法。这也将提供一个更健壮的测试套件。您需要仔细考虑该方法是否真的需要public(这意味着它可以并且应该在其自己的程序集之外被引用)。
接口只是为你做了一个契约,基本上是一个承诺,你的实现将提供对一组指定接触点(方法、属性等)的访问,没有行为规范。只要你遵守诺言,你可以自由地做任何你想做的事。
另一方面,除了契约之外,基类至少指定了一些在类中编码的行为(除非一切都是抽象的,但那是另一回事)。使方法成为虚方法仍然使您能够调用基类的实现,并且仍然提供您自己的代码。
这种行为继承基本上是多重继承在现代OOP中是禁忌的原因,而多接口实现相对常见。
也就是说,你需要权衡你是想提取一个合约,还是想提取一些行为,对于特定的情况,答案应该是显而易见的。
对于IParser
/Parser
对,首先,它们非常适合单元测试和依赖注入,其次,它们不需要为创建类收费,所以可以随意创建任意多的类。
通过对接口进行编程,您可以在单元测试和松散耦合代码中轻松地模拟/存根(并且因此具有更高的灵活性),从字面上免费的(唯一的缺点是需要管理更多的工件)。
接口和继承是两个独立的东西,即使可以互换使用,也不是个好主意。通过标记方法virtual
,您实际上是在告诉其他人,他们不仅可以自由地在他们的实现中更改(覆盖)该方法,而且您实际上期望他们(并且是您吗?)。
这样的设计会带来相当严重的后果,所以除非你明确需要它——否则你不应该使用它。试着坚持为界面编程。
一个好的面向对象设计原则是,你应该为接口编程(设计契约, Liskov替代原则),并且更喜欢组合而不是继承(不仅你的类应该实现接口/抽象类,而且还应该由这些实现组成)。
值得注意的是,您的Parser
示例是隐藏在抽象(无论是接口还是基类)后面的完美候选。从消费者的角度来看,如何创建数据并不重要——目前您可能认为它只是XML流,但需求往往会变化(和/或增长),您可能很快就会发现自己实现了二进制文件解析器、数据流挖掘解析器和其他东西。现在就把它做好,省时省事。