接口隔离原则
概念:英文全称是InterfaceSegregation Principles(ISP)。ISP的定义是:客户端不应该依赖它不需要的接口。另一种定义:类间的依赖关系应该是建立在最小的接口上。接口隔离原则意图是将庞大,臃肿的接口拆分成更小的和更具体的接口,这样客户将只需要根据他们的需求选择特定的接口。该原则的目的是系统解开耦合,从而容易重构,更改和重新部署。简单来说,就是接口尽量细化,同时接口中的方法尽量少。
可能你会说,这与单一职责原则不是相同的吗?不对,它们审视的角度不同,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离要求接口的方法尽量少。例如一个接口的职责可能包含10个方法,这10个方法都放在一个接口中,并且提供给多个模块访问,各个模块按照规定的权限来访问,在系统外通过文档约束“不使用的方法不要访问”,按照单一职责原则是允许的,按照接口隔离是不允许的,因为它要求“尽量使用多个专门的接口”。专门的接口指什么?就是指提供给每个模块的都应该是单一接口,提供给几个模块就应该有几个接口,而不是建立一个庞大的臃肿的接口,容纳所有的客户端访问。举例如下:
案例:星探选美女
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;
}
@Override
public void goodLooking() {
System.out.println(this.name+" 脸蛋漂亮");
}
@Override
public void niceFigure() {
System.out.println(this.name+" 身材好");
}
@Override
public void greatTemperament() {
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);
}
@Override
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 girl = new PettyGirl("刘亦菲");
AbstractSearcher searcher = new Searcher(girl);
searcher.show();
}
}
运行结果:
------美女信息-----
刘亦菲 脸蛋漂亮
刘亦菲 身材好
刘亦菲 气质好
刘亦菲 脸蛋漂亮
刘亦菲 身材好
刘亦菲 气质好
分析:程序能够正常运行,回头来想一想这个程序有没有问题,思考一下IPettyGirl这个接口,这个接口是否做到了最优化设计?没有,还可以对接口进行优化。因为大众审美观可能不同,比如身材好,脸蛋好的女生算美女,但是长相一般,但气质很好的女生应该也算美女吧,这时会发现IPettyGirl过于庞大,封装过度了,因此IPettyGirl接口可以再优化,将其拆分为两个接口,一类是脸蛋好,身材好,气质差的女生为一类美女,接口为IGoodBodyGirl,另一类是长相一般,气质超好的女生为一类美女,接口为IGreatTemperamentGirl.如下:
两种类型的美女定义:
public interface IGoodBodyGirl {
//脸蛋好
public void goodLooking();
//身材好
public void niceFigure();
}
public interface IGreatTemperamentGirl {
//气质好
public void greatTemperament();
}
public class PettyGirl implements IGreatTemperamentGirl,IGoodBodyGirl{
private String name;
public PettyGirl(String name) {
this.name = name;
}
public void goodLooking() {
System.out.println(this.name+" 脸蛋漂亮");
}
public void niceFigure() {
System.out.println(this.name+" 身材好");
}
public void greatTemperament() {
System.out.println(this.name+" 气质好");
}
}
分析:拆分后,灵活性提高了,不管以后是要外形美还是气质好的美女都可以通过PettyGirl定义。
以上把一个臃肿的接口变更为两个独立的接口所依赖的原则就是接口隔离原则,让星探AbstractSearcher依赖两个专用的接口比依赖一个综合的接口要灵活。
再看一个案例:关闭流
public void put(String url,Bitmap bitmap){
FileOutputStream fileOut=null;
try {
fileOut=new FileOutputStream(cacheDir+url);
}catch (FileNotFoundException e){
e.printStackTrace();
}finally {
if(fileOut!=null){
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
} //end if
}//end if finally
}
分析:这个方法相信在很多地方都能见到,finally里面的代码可读性非常差,因为嵌套的层级比较多,假如有几十个这样put方法,就需要finally几十次,这怎么能忍受!我们可能知道Java中有一个Closeable接口,该接口表示了一个可关闭的对象,它只有一个close方法,FileOutputStream实现了该Closeable接口,那只要建一个方法统一来关闭这些对象不就可以了,如下工具类:
public final class CloseUtils {
private CloseUtils(){}
public static void closeQuietly(Closeable closeable){
if(null != closeable){
try {
closeable.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
String cacheDir="xx";
public void put(String url,Bitmap bitmap) {
FileOutputStream fileOut = null;
try {
fileOut = new FileOutputStream(cacheDir + url);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
CloseUtils.closeQuietly(fileOut);
}
}
分析:CloseUtils的closeQuietly方法的基本原理就是依赖于Closeable抽象而不是具体实现,并且建立在最小化依赖原则的基础上,它只需要知道这个对象是可关闭的,其他的一概不关心,也体现了接口隔离原则。
总结:接口隔离原则是对接口的定义,接口是我们设计时对外提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。