摘要本文系统梳理抽象类与接口的核心概念、相似点与本质区别并结合实际开发场景给出选型建议帮助你在面向对象设计中做出更合理的取舍。一、抽象类不能被实例化的“半成品”抽象类是特殊的类只是不能被实例化除此以外它具有类的其他特性。更重要的是抽象类可以包含抽象方法这是普通类所不能的。抽象方法只能声明于抽象类中且不包含任何实现派生类必须覆盖它们。另外抽象类可以派生自另一个抽象类既可以覆盖基类的抽象方法也可以不覆盖如果不覆盖则其派生类必须覆盖它们。一句话理解抽象类描述的是“是什么”它把一组紧密相关的类中共同的、可复用的部分沉淀下来同时把不确定的行为留给子类实现。二、接口行为的规范与契约接口是引用类型形式上类似于类。它与抽象类有三点相似之处都不能被实例化都包含未实现的方法声明派生类都必须实现未实现的方法——抽象类中是抽象方法接口中则是所有成员不仅是方法还包括其他成员。此外接口还有如下特性接口除了可以包含方法之外还可以包含属性、索引器、事件而且这些成员都被定义为公有的除此之外不能包含任何其他成员例如常量、域、构造函数、析构函数、静态成员一个类可以直接继承多个接口但只能直接继承一个类包括抽象类。三、抽象类和接口的区别抽象层次不同。类是对对象的抽象可以把抽象类理解为“把类当作对象”再抽象出来的类而接口只是一个行为的规范或规定。微软的自定义接口总是以 able 结尾正是为了表达“我能做……”这类能力语义。抽象类更多定义在一系列紧密相关的类之间而接口大多用于关系疏松、但都实现某一功能的类中。继承语义不同。接口基本上不具备继承的任何具体特点它仅仅承诺了能够调用的方法。数量限制不同。一个类一次可以实现若干个接口但是只能扩展一个父类。回调支持不同。接口可以用于支持回调而继承并不具备这个特点。密封性不同。抽象类不能被密封。虚方法默认值不同。抽象类实现的具体方法默认为虚的但实现接口的类中的接口方法却默认为非虚的当然也可以显式声明为虚的。接口成员实现要求不同。与非抽象类类似抽象类也必须为在该类基类列表中列出的接口的所有成员提供自己的实现。但是允许抽象类将接口方法映射到抽象方法上。设计原则不同。抽象类实现了 OOP 中的一个原则把可变的与不可变的部分分离。抽象类和接口定义为不可变的部分而把可变的部分交给子类去实现。接口应保持专一。好的接口定义应该具有专一功能性而不是多功能否则会造成接口污染。如果一个类只是实现了这个接口中的一个功能却不得不去实现接口中的其他方法这就叫接口污染。优先使用组合。尽量避免使用继承来实现组件功能而是使用黑箱复用即对象组合。因为继承层次增多后最直接的后果就是当你调用这个类群中的某一类时就必须把它们全部加载到栈中后果可想而知可结合堆栈原理理解。同时可以留意到微软在构建一个类时很多时候用到了对象组合的方法。比如 ASP.NET 中Page 类有 Server、Request 等属性但其实它们都是某个类的对象。使用 Page 类的这个对象来调用另外的类的方法和属性这是非常基本的一个设计原则。抽象类实现接口的映射。如果抽象类实现接口则可以把接口中的方法映射到抽象类中作为抽象方法而不必实现而在抽象类的子类中实现接口中的方法。四、抽象类和接口的使用场景如果预计要创建组件的多个版本则创建抽象类。抽象类提供了简单的方法来控制组件版本。如果创建的功能将在大范围的全异对象间使用则使用接口如果要设计小而简练的功能块也使用接口。如果要设计大的功能单元则使用抽象类如果要在组件的所有实现间提供通用的已实现功能则使用抽象类。抽象类主要用于关系密切的对象而接口适合为不相关的类提供通用功能。五、一张表看懂如何选型对比维度抽象类接口能否实例化否否能否包含实现可以且可包含字段、构造函数通常只声明成员成员默认公有继承数量只能继承一个父类一个类可实现多个接口适用关系紧密相关的类关系疏松但具备同一能力的类典型用途版本演进、通用功能复用能力契约、跨类型通用功能六、小结抽象类与接口并不是非此即彼的对立关系而是面向对象设计中两种互补的抽象手段。把握住“抽象类表达是什么、接口表达能做什么”这条主线再结合版本演进、复用需求和类之间的关系紧密度来判断就能在实际项目中做出更清晰、更易维护的设计选择。