什么是接口隔离原则?
接口隔离原则有两种定义:
Clients should not be forced to depend upon interfaces that they don't use.(客户端不应该强行依赖它不需要的接口) The dependency of one class to another one should depend on the smallest possible interface.(类间的依赖关系应该建立在最小的接口上) |
这两个定义可以归纳为一个意思:建立单一接口,不要建立臃肿庞大的接口。也就是说,接口尽量细化,同时接口中的方法尽量少。
接口隔离原则看起来似乎和单一职责原则撞一块了,但是:
- 单一职责原则要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分。
- 接口隔离原则要 求接口的方法尽量少,哪怕一个类或接口只有一个职责,但却有较多的方法,这再接口隔离原则中是不允许的。
为什么要用接口隔离原则?
以星探寻找美女为例:
不遵循接口隔离原则的设计
美女接口:
美女接口作了美女的定义。
public interface IPettyGirl {
//要有姣好的面孔
public void goodLooking();
//要有好身材
public void niceFigure();
//要有气质
public void greatTemperament();
}
美女实现类:
美女的具体要求。
public class PettyGirl implements IPettyGirl {
private String name; //美女名字
public PettyGirl(String _name){
this.name=_name;
}
//脸蛋漂亮
public void goodLooking() {
System.out.println(this.name + "---脸蛋很漂亮!");
}
//气质要好
public void greatTemperament() {
System.out.println(this.name + "---气质非常好!");
}
//身材要好
public void niceFigure() {
System.out.println(this.name + "---身材非常棒!");
}
}
星探抽象类:
public abstract class AbstractSearcher {
protected IPettyGirl pettyGirl;
public AbstractSearcher(IPettyGirl _pettyGirl){
this.pettyGirl = _pettyGirl;
}
//搜索美女,列出美女信息
public abstract void show();
}
星探实现类:
public class Searcher extends AbstractSearcher{
public Searcher(IPettyGirl _pettyGirl){
super(_pettyGirl);
}
//展示美女的信息
public void show(){
System.out.println("--------美女的信息如下:---------------");
//展示面容
super.pettyGirl.goodLooking();
//展示身材
super.pettyGirl.niceFigure();
//展示气质
super.pettyGirl.greatTemperament();
}
}
场景类:
public class Client {
//搜索并展示美女信息
public static void main(String[] args) {
//定义一个美女
IPettyGirl yanYan = new PettyGirl("嫣嫣");
AbstractSearcher searcher = new Searcher(yanYan);
searcher.show();
}
}
运行结果:
--------美女的信息如下:---------------
嫣嫣---脸蛋很漂亮!
嫣嫣---身材非常棒!
嫣嫣---气质非常好!
OK,这么一个选美的过程完成了。
但是仔细一想,这个接口的设计还是有问题的。这里选出的美女要容貌、身材、气质都很好。但是,如果容貌、气质都很好,身材稍逊就不算美女了吗?不然的话,怎么会有热辣型美女、气质型美女呢?IPettyGirl的设计明显是有缺陷的,过于庞大了,容纳了一些可变的因素。
遵循接口隔离原则的设计
根据接口隔离原则,重新设计。
将IPettyGirl接口拆分为两个接口,外观型美女IGoodBodyGirl,脸蛋和身材极棒;气质型美女IGreatTemperamentGirl,谈吐和修养都非常高。
两种类型的美女定义接口:
public interface IGoodBodyGirl {
//要有姣好的面孔
public void goodLooking();
//要有好身材
public void niceFigure();
}
public interface IGreatTemperamentGirl {
//要有气质
public void greatTemperament();
}
完美的美女接口:
public class PettyGirl implements IGoodBodyGirl,IGreatTemperamentGirl {
private String name; //美女都有名字
public PettyGirl(String _name){
this.name=_name;
}
//脸蛋漂亮
public void goodLooking() {
System.out.println(this.name + "---脸蛋很漂亮!");
}
//气质要好
public void greatTemperament() {
System.out.println(this.name + "---气质非常好!");
}
//身材要好
public void niceFigure() {
System.out.println(this.name + "---身材非常棒!");
}
}
把一个臃肿的接口变更为两个独立的接口所依赖的原则就是接口隔离原则,让星探 AbstractSearcher依赖两个专用的接口比依赖一个综合的接口要灵活。接口是设计时对外 提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。
深究接口隔离原则
接口隔离原则是对接口进行规范约束,其包含以下4层含义:
- 接口要尽量小
这是接口隔离原则的核心定义,不出现臃肿的接口(Fat Interface)。
但是,小是有限度的,根据接口隔离原则拆分接口时,首先必须满足单一职责原则。
- 接口要高内聚
高内聚就是提高接口、类、模块的处理能力,减少对外的交互。
具体到接口隔离原则就是,要求在接口中尽量 少公布public方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越 少,同时也有利于降低成本。
- 定制服务
一个系统或系统内的模块之间必然会有耦合,有耦合就要有相互访问的接口(并不一定 就是Java中定义的Interface,也可能是一个类或单纯的数据交换),设计时就需要为各个访问者(即客户端)定制服务。
- 接口设计是有限度的
接口的设计粒度越小,系统越灵活,这是不争的事实。但是,灵活的同时也带来了结构 的复杂化,开发难度增加,可维护性降低,这不是一个项目或产品所期望看到的,所以接口 设计一定要注意适度,这个“度”如何来判断呢?根据经验和常识判断,没有一个固化或可测 量的标准。
参考:
【1】:《设计模式之禅》
【2】:设计模式六大原则(4):接口隔离原则
【3】:聊聊设计模式原则(二) -- 接口隔离原则