接口的本质是一个功能的提供者与服务的调用者(消费者)之间的一个契约(contract)
(协议、标准)
所以接口的所有成员必须是public的
举例1 可迭代接口
Foreach遍历的唯一要求是遍历对象是“可迭代的”,这可以视作一个调用者需求
提供者必须满足这个需求,遍历对象与变量操作之间通过接口达成这一契约
对象继承这个接口时,说明其能满足这个需求
整型数组与对象数组因为类型不同,无法共用同一个求和方法
但他们都是可迭代的,都实现(对接口说实现不说继承)了IEnumerable可迭代接口
含遍历的方法只要求对象可迭代 (调用方要求)
数组与ArrayList都实现了可迭代接口 (供应方条件)
所以在传入共用方法时,使用此接口作为类型
二者就可以共用一个方法来处理求和
接口与耦合
引入接口,有效降低耦合
“让调用者和提供者只有部分功能连在一起!”
创建一个叫IPhone 的接口,规定了被称作“手机”的类需要有哪些方法
之后创建一个名叫诺基亚手机的类,实现这个接口
编辑器会出现实现接口的提示,点击后会自动生成接口中规定的四个方法体
术语解释:接口的【实现】 = 抽象类的【继承】
在使用用接口作为参数类型的方法时(如图中的构造器)
就可以传输任何一个实现了该接口的实例对象(用继承看就是作为抽象类的接口的子类)
这样一个品牌的手机(实现了手机接口的类)出问题时,就不会影响手机这个接口和其他实现此接口的类,只需要在类“手机用户”的构造器中换一个参数,不用打开修改手机用户类的代码
降低了手机用户类与具体手机类的耦合性
使用反射可以进一步降低耦合性,设置一行代码都不用改(待续)
接口让功能的提供方可以被替换
提供方出现问题时,可以直接替换,降低耦合性
依赖反转原则(待续)
紧耦合
单元测试(未完)
单元测试是依赖反转原则在开发中的直接应用
方法抽象程度比较:
接口方法 -> abstract抽象方法->virtual虚方法-> override重写方法
从抽象到实现
接口隔离原则
将大接口拆分,拆为多个单一功能,将“本质不同”的功能隔离,再使用接口封装
接口的设计问题
甲方(功能调用者):我不会多要(软性约束)(编译器无法检验甲方有没有多要)
乙方(功能提供者):我不会少给(硬性约束)(有方法没完全实现会直接报错)
接口的这个设计问题,需要通过人为规定设计原则来回避
检验方法:看传给调用者的接口类型中,有没有一直没被调用过的函数成员
如果有,说明传入的接口只有一部分被调用了,说明接口类型“太大了”,有冗余
根据“大接口”的产生原因不同会有两种问题情况
情况1.大接口是设计失误
将太多的功能包含在接口中了,此时,实现这个接口的类就违反了“单一职责原则”
接口隔离与单一职责的关系
接口隔离原则与单一职责原则互为两面
单一职责原则 是在功能提供者的视角
接口隔离原则 是在功能调用者的视角
解决方法
将大接口拆分,拆为多个单一功能,将“本质不同”的功能隔离,再使用接口封装
作为司机,“大接口”I坦克中,有一个永远用不到的功能,Fire()
根据接口隔离原则,应该将“大接口”的两个功能,开炮和跑,分裂成两个功能
将接口使用多继承分为两级
武器接口与载具接口各自拥有单一功能,开火和跑
坦克接口多继承自武器接口和坦克接口
接口多继承
C#中,类与类的继承只能有一个基类
但对接口的继承,可以有多个基接口
接口是一个契约,表示必须要有XX功能
情况2.大接口是由小接口合并/扩展而来
案例1 复合接口
例如将应该传入司机类作为字段成员类型的单一职责接口【载具】
更换为了载具武器复合接口【坦克】,或在原单一接口基础上增加了功能的扩展接口
这样除坦克外其他能满足跑的功能需求的类,如汽车和卡车,就无法传入司机的字段中
案例2 扩展接口
继承自原单一职责接口,但比原接口增加了一些功能,如:
ICollection接口,是一个继承自IEnumberable 可遍历接口的子接口
比可遍历多了几个功能:
可以知道自己装了几个元素
可以将元素复制到一个数组array中
ex:成员类、自定义可迭代类型及其迭代器设置,参见C#inVS2/接口与抽象类/接口隔离原则2
显式接口实现
是C#语言独有的功能
普通接口实现方式:
显式接口实现
显式接口实现,只有把这个实例装在IKiller类型变量中时,这个方法才可以被调用
例如之前的坦克开火,也可以设计成只有把坦克视作武器(装在weapon类型变量中)时,才能调用开炮的fire()方法,这样将其装在其他容器中时,就可以绝对安全,无法调用危险方法
显式实现标注方法:在方法名前加上其所属的接口名
在WarmKiller类中找不到kill方法,因为以非特定容器(Killer)来装实例时,无法调用显式实现的方法
装在方法对应的特定接口类型的变量中时,就可以看到并调用显式实现的方法
配合强制类型转换
要调用实例的其他方法时,可使用强制类型转换将特定类型变回通用类型
可以这样在多层级接口中转换
将同一个实例从不同角度视作不同事物,互不干扰