接口隔离原则的定义
首先,我们需要知道什么是接口,接口分两种:
- 实例接口(Object Interface),在Java中声明一个类,然后用new关键字产生一个实例,它是对一个类型的事物的描述,这是一种接口。比如你定义Person这个类,然后使用Person zhangSan= new Person();产生了一个实例,这个实例要遵循的标准就是Person这个类,Person这个类就是zhangSan的接口。疑惑?看不懂?不要紧,那是因为让Java语言浸染的时间太长了,只要知道从这个角度看,Java中的类也是一种接口。
- 类接接口(Class Interface),Java中经常使用interface定义的接口。
主角已经清楚了,那什么是隔离?它有两种定义:
- 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. (类间的依赖关系应该建立在最小的接口上。)
新事物的定义一般都比较难理解,晦涩难懂是正常的。我们把这两个定义剖析一下,先说第一种定义:”客户端不应该依赖它不需要的接口“,那依赖是什么?依赖它需要的接口,客户端需要什么接口就提供什么接口,把不需要的接口剔除掉,那就需要对接口进行细化,保证其纯洁性;再看第二种定义:”类间的依赖关系应该建立在最小的接口上“,它要求是最小的接口,也是要求接口细化,接口纯洁,与第一个定义如出一辙,只是一个事物的两种不同描述。
我们把这两个定义概括为一句话:建立单一接口,不要建立臃肿庞大的接口。再通俗点讲:接口尽量细化,同时接口中的方法尽量少。接口隔离原则要求接口中的方法尽量地少。这里要说明的是,隔离不是拆分,单一职责原则体现在拆分上,要求将职责尽量地分离;接口隔离原则则要求接口中的不需要用到的方法尽量地少,至于少到什么程度,在逻辑上是不可度量的,需要结合实际情况考虑。
美女何其多,观点各不同
下面我们建立一个例子来说明接口隔离对我们有什么要求。现在男生对小姑娘的称呼,使用频率最高的应该是”美女“了吧,每个人心目中美女的定义各不相同,但似乎都有一个通用的尺度,或者称为标准,这个标准用面向对象的接口来描述。一名美女需要具备如下:面貌、身材、气质,我们用类图来实现。
定义一个IPrettyGirl接口,声明所有美女都应该有goodLooking、niceFigure、greatTemperament,然后定义了一个抽象类AbstractSearcher,其作用就是搜寻美女并显示信息,并实现其具体类。
代码清单1:IPrettyGirl.java (美女接口)
/** * 美女类接口 * @author Barudisshu */public interface IPrettyGirl { /**要有较好的面孔*/ public void goodLooking(); /**要有好身材*/ public void niceFigure(); /**要有气质*/ public void greatTemperament(); }
代码清单2:PrettyGirl.java (美女实现类)
/** * 美女 * * @author Barudisshu */public class PrettyGirl implements IPrettyGirl { //美女都有名字 private String name; public PrettyGirl(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 + "---气质非常好!"); }}
代码清单3:AbstractSearcher.java (星探抽象类)
/** * * @author Barudisshu */public abstract class AbstractSearcher { protected IPrettyGirl prettyGirl; public AbstractSearcher(IPrettyGirl prettyGirl) { this.prettyGirl = prettyGirl; } /** * 搜索美女,列出美女的信息 */ public abstract void show();}
代码清单4:Searcher.java (星探实现类)
/** * * @author Barudisshu */public class Searcher extends AbstractSearcher{ public Searcher(IPrettyGirl prettyGirl) { super(prettyGirl); } @Override public void show() { System.out.println("------美女的信息如下:------"); //展示面容 super.prettyGirl.goodLooking(); //展示身材 super.prettyGirl.niceFigure(); //展示气质 super.prettyGirl.greatTemperament(); } }
代码清单5:Client.java (模拟场景)
/** * * @author Barudisshu */public class Client { public static void main(String[] args) { //定义一个美女 IPrettyGirl yanYan = new PrettyGirl("嫣嫣"); AbstractSearcher searcher = new Searcher(yanYan); searcher.show(); }}
星探搜索美女的运行结果如下所示:
星探寻找美女的程序开发完毕了,运行结果也正确了。我们看看这个程序是否是最优的,接口隔离原则的要求是接口的方法应该尽量地少,把不需要的方法尽量隔离。
我们的审美观还在变,美女的定义也在变。比如唐朝的杨贵妃,算是公认的美女了,但是在这里却不符合,为什么?胖啊!但是胖并不影响她入选中国的四大美女,说明当时的审美观与现在存在差异。另一方面,当我们看到一个女孩长得很漂亮,身材也很好,我们觉得她”看“上去是个美女,但当我们发现她粗言秽语、随地仍东西等行为,我们还会认为他是个美女吗?所以,我们需要把握好尺度。
分析到这里,我们发现接口IPrettyGirl接口还是过于庞大,容纳了一些可变因素,我们需要把定义进行分离,我们把美女分割成外在美和内在美的行为,分别定义两个接口IGoodBodyGirl和IGreatTemperamentGirl。
代码清单6:两种类型美女的定义。
/** * * @author Barudisshu */public interface IGoodBodyGirl { /** * 要有较好的面孔 */ public void goodLooking(); /** * 要有好的身材 */ public void niceFigure();}public interface IGreatTemperamentGirl { /** * 要有气质 */ public void greatTemperament();}
代码清单7:PrettyGirl.java (最标准的美女)
/** * 标准的美女 * * @author Barudisshu */public class PrettyGirl implements IGoodBodyGirl, IGreatTemperamentGirl { //每个美女都有名字 private String name; public PrettyGirl(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 + "---气质非常好!"); }}
代码清单8:AbstractSearcher.java (重新定义星探抽象类)
/** * * @author Barudisshu */public abstract class AbstractSearcher { protected IGoodBodyGirl goodBodyGirl; protected IGreatTemperamentGirl greatTemperamentGirl; /** * 外形美的美女 */ public AbstractSearcher(IGoodBodyGirl goodBodyGirl) { this.goodBodyGirl = goodBodyGirl; } /** * 气质美的美女 */ public AbstractSearcher(IGreatTemperamentGirl greatTemperamentGirl) { this.greatTemperamentGirl = greatTemperamentGirl; } /** * 搜索美女,列出美女的信息 */ public abstract void show();}
代码清单10:Searcher.java
/** * * @author Barudisshu */public class Searcher extends AbstractSearcher { public Searcher(IGoodBodyGirl goodBodyGirl) { super(goodBodyGirl); } public Searcher(IGreatTemperamentGirl greatTemperamentGirl) { super(greatTemperamentGirl); } @Override public void show() { System.out.println("------美女的信息如下:------"); if (super.goodBodyGirl != null) { super.goodBodyGirl.goodLooking(); super.goodBodyGirl.niceFigure(); } if (super.greatTemperamentGirl != null) { super.greatTemperamentGirl.greatTemperament(); } }}
代码清单11:Client.java (模拟场景)
/** * * @author Barudisshu */public class Client { public static void main(String[] args) { //定义一个气质美女 IGreatTemperamentGirl yanYan = new PrettyGirl("嫣嫣"); AbstractSearcher searcher = new Searcher(yanYan); searcher.show(); }}
通过这样的重构后,不管以后是要气质的美女还是要外形的美女,都可以保持接口的稳定。当然,你可能要说,以后可能审美观再发生变化,只要脸蛋好看就是美女,那这个IGoodBody接口还是要修改啊,确实是,但是设计是有限度的,好比如提出行业规范一样,不能无限地考虑未来变更的情况,否则就会陷入设计的泥潭中不能自拔。
保持接口的纯洁性
接口原则是对接口进行规范约束,其包含如下4层含义:
- 接口要尽量小
- 接口要高内聚
- 定制服务
- 接口设计是有限度的
根据接口原则拆分接口时,首先必须满足单一指责原则。