我有一个数字Processor
类,可以做两种非常不同的事情,但是是从通用代码中调用的(“控制反转”情况)。
我想知道在决定它们是否都应该继承自BaseProcessor
或实现IProcessor
为接口时,我应该了解哪些设计考虑因素(或者对您来说是对用户有用的认识)。
Answers:
通常,规则如下所示:
为了更具体地说明这一点,让我们看一个例子。的System.Drawing.Bitmap
类是-一个图像(并且同样地,其从继承Image
类),但它也可以做的配置,所以它实现了IDisposable
接口。它还可以进行序列化,因此可以从ISerializable
接口实现。
但是实际上,接口通常用于模拟C#中的多重继承。如果您的Processor
类需要继承自System.ComponentModel.Component
,则您别无选择,只能实现一个IProcessor
接口。
事实是,接口和抽象基类都提供了指定特定类可以做什么的协定。众所周知,接口必须声明此合同,但这是不正确的。在我看来,最大的好处是抽象基类允许您为子类提供默认功能。但是,如果没有有意义的默认功能,则没有什么可以阻止您将方法本身标记为abstract
,要求派生类自己实现它,就像它们要实现接口一样。
对于此类问题的答案,我经常转向.NET Framework设计指南,其中有关于在类和接口之间进行选择的说明:
通常,类是公开抽象的首选构造。
接口的主要缺点是,在允许API演变时,它们不如类灵活。交付接口后,其成员集合将永久固定。接口的任何添加都会破坏实现接口的现有类型。
一个类提供了更大的灵活性。您可以将成员添加到已经交付的类中。只要方法不是抽象的(即,只要您提供该方法的默认实现),任何现有的派生类都将继续保持不变。
[。。。]
支持接口的最常见论点之一是,它们允许将协定与实现分开。但是,该参数错误地假定您无法使用类将合同与实现分开。驻留在独立于其具体实现的单独程序集中的抽象类是实现此类分离的好方法。
他们的一般建议如下:
克里斯·安德森(Chris Anderson)对此最后一个宗旨表示特别同意,认为:
抽象类型的版本要好得多,并允许将来扩展,但是它们也会消耗您唯一的基本类型。当您真正定义两个对象之间随时间变化的契约时,接口是合适的。抽象基类型更适合为一系列类型定义公共基。
理查德
为什么选择它们?我将有一个IProcessor接口作为已发布的类型(供系统中的其他位置使用);如果碰巧您的IProcessor的各种CURRENT实现都有共同的行为,那么抽象BaseProcessor类将是实现该共同行为的真正好地方。
这样,如果您将来需要一个尚未用于BaseProcessor服务的IProcessor,它就不必拥有它(并可能将其隐藏)...但是那些想要它的人可以拥有它...减少重复的代码/概念。
只是我谦虚的观点。
干杯。基思
接口是一个“契约”,它们确保某些类实现所需的一组成员(属性,方法和事件)。
基类(具体的还是抽象的,没关系)是某些实体的原型。这些实体代表了某些实际的物理或概念实体中的共同点。
什么时候使用接口?
每当某种类型需要声明至少具有某些行为和属性时,消费者应该关心并使用它们来完成某些任务。
何时使用基类(具体和/或抽象)
每当一组实体共享相同的原型时,意味着B继承A是因为B是具有差异的A,但是B可以标识为A。
例子:
让我们谈谈表格。
我们接受可回收表=>必须使用“ IRecyclableTable”之类的接口定义此表,以确保所有可回收表都将具有“ Recycle”方法。
我们希望桌面表=>必须使用继承定义。“桌面表”是“表”。所有表都具有相同的属性和行为,而桌面表将具有相同的属性和行为,从而增加了使桌面表与其他类型的表不同的功能。
我可以谈论关联,即对象图中这两种情况的含义,但以我谦卑的观点,如果我需要从概念的角度给出论据,我会用这种论证来回答。
IRecyclable
接口,因为似乎几个不相关的对象可能需要可回收(即,其他不代表表的类)。然后,我仍然可以从Table
该类中实现所有表,包括DesktopTable
andPicnicTable
和FoldingTable
。
我在设计选择上不太擅长,但是如果有要求,我将更喜欢在仅扩展成员的情况下实现iProcessor接口。如果还有其他功能不需要扩展,则从baseprocessor继承是更好的选择。
除了设计纯度,您只能继承一次,因此,如果您需要继承某个框架类,那么这个问题就没有意义了。
如果可以选择,可以实用地选择保存最多键入内容的选项。无论如何,它通常是纯粹的选择。
编辑:
如果基类将具有某种实现,那么这可能会很有用,如果仅是抽象知识,那么它也可能是接口。
如果选择的内容无关紧要,请始终选择“界面”。它具有更大的灵活性。它可以保护您免受将来的更改(如果您需要更改基类中的某些内容,则继承的类可能会受到影响)。它还可以更好地封装细节。如果您正在使用某种依赖注入控制反转,则它们倾向于使用接口。
或者,如果无法避免继承,将它们一起使用可能是一个好主意。创建一个实现接口的抽象基类。在您的情况下,ProcessorBase实现一个IProcessor。
与ASP.NET Mvc中的ControllerBase和IController相似。