在面向对象编程中,抽象类和接口是两个经常被用到的语法概念,是面向对象的四大特性,以及很多设计模式、设计思想、设计原则编程实现的基础。如,可以使用接口来实现面向对象的抽象特性、多态特性和基于接口而非实现的设计原则;使用抽象类来实现面向对象的继承特性和模板设计模式等
虽然不是所有的语言都支持接口和抽象,如C++只支持抽象不支持接口,Python这样的动态编程语言既不支持抽象类又不支持接口;但仍然可以通过一些手段来模拟实现这两个语法概念
抽象类和接口分别是什么,区别是什么
-
抽象类不允许被实例化,只能被继承,既不能new一个抽象类的对象出来,编译会报错
-
抽象类可以包含属性和方法,方法及可以包含代码实现,也可以不包含,不包含代码实现的方法叫抽象方法
-
子类继承抽象类,必须实现抽象类中所有的抽象方法
-
接口不能包含属性(即成员变量)
-
接口只能声明方法,方法不能包含代码实现
-
类实现接口的时候,必须实现接口中声明的所有方法
除了语法上,从设计的角度看也有较大区别:抽象类实际上就是一种特殊的类,这种类不能被实例化为对象,只能被子类继承,而继承是一种非is-a关系,相对于抽象类的is-a,接口表示一种has-a关系,表示具有某些功能。对于接口,有一个更加形象的叫法,就是协议constract
抽象类更多是为了代码复用,而接口更侧重于解耦。 接口是对行为的一种抽象,相当于一组协议,类比API接口,调用者只需要关注抽象的接口,不需要了解具体的实现,具体的实现代码对调用者透明,接口实现了约定和实现相分离,可以降低代码的耦合性,提高代码的可扩展性。
实际上,接口是一个比抽象类应用更加广泛的知识点,如“基于接口而非实现编程”就是一条几乎天天都会用到且能极大提高代码灵活性扩展性的设计思想。
模拟抽象类和接口这两个语法概念
C++中只有抽象类没有接口,但可以通过抽象类模拟接口。===> 纯虚函数
先来回忆一下接口的定义:接口中没有成员变量,没有方法实现,实现接口的类必须实现接口中的所有方法。只要满足这几点,从设计的角度上说,就可以叫做接口
class Strategy
{
public:
~Strategy();
virtual void algorithm() = 0;
proctected:
Strategy();
};
抽象Strategy没有定义任何属性,且所有方法都声明为virtual类型,这样所有的方法都不能有代码实现,并且所有继承这个抽象类的子类都要实现这些方法。
为什么要基于接口而非实现编程
从本质来看,接口时一组协议或者约定,是功能提供者提供给使用者的一个功能列表。这样可以实现接口和实现相分离,封装不稳定的实现,暴露稳定的接口。上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,一次来降低耦合性,提高扩展性。
实际上,“基于接口而非实现编程”的另一个表述方式是“基于抽象而非实现编程”,后者的表述方式更能体现这条原则的设计初衷。在软件开发中,需求会不断变化,这就是考验代码设计好坏的一个标准。越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。而抽象就是提高代码扩展性、灵活性、可维护性最有效的手段之一。
具体做到下面三点:
- 函数的命名不能暴露任何实现细节
- 封装具体的实现细节
- 为实现类定义抽象的接口。具体的实现类都依赖同意的接口定义。
使得能够在替换具体接口实现的时候,不需要任何接口定义的改动。
继承和组合
继承层次过深、继承关系拓宇复杂会影响到代码的可读性和可维护性,实际上可以利用组合、接口、委托解决继承的问题