接口隔离原则
是指用多个专门的接口,而不要使用单一的总接口,客户端不应该依赖它不需要的接口,我们根据定义可以概括为以下几点:
1,建立单一接口,不要建立臃肿庞大的接口。
2,一个类对另一个类的依赖应该建立在最小的接口之上。
3,尽量细化接口,接口中的方法尽量少(不是越少越好,适度)。
接口隔离原则符合我们常说的高内聚,低耦合的设计思想,可以使类具有很好的可读性,可扩展性和可维护性。下面我们来看一段代码,来描述一个美女:
public interface PrettyGirl {
//要有漂亮的脸蛋
void goodLooking();
//要有火辣的身材
void niceFigure();
//要有气质
void greatTemperament();
}
下面是美女的实现类:
public class Girl implements PrettyGirl {
private String name;
//美女都有名字
public Girl(String name){
this.name = name;
}
//脸蛋漂亮
public void goodLooking() {
log.info(this.name + "---脸蛋很漂亮!");
}
//气质要好
public void greatTemperament() {
log.info(this.name + "---气质非常好!");
}
//身材要好
public void niceFigure() {
log.info(this.name + "---身材非常棒!");
}
}
有美女,就有喜欢看美女的我,其具体实现代码所示:
public abstract class AbstractPerson {
protected PrettyGirl prettyGirl;
public AbstractPerson(PrettyGirl prettyGirl){
this.prettyGirl = prettyGirl;
}
//搜索美女,列出美女信息
public abstract void look();
}
public class LaoXu extends AbstractPerson{
public LaoXu(PrettyGirl prettyGirl){
super(prettyGirl);
}
public void look(){
log.info("--------美女的信息如下:---------------");
//展示面容
super.prettyGirl.goodLooking();
//展示身材
super.prettyGirl.niceFigure();
//展示气质
super.prettyGirl.greatTemperament();
}
}
场景类如下:
public class Client {
//搜索并展示美女信息
public static void main(String[] args) {
//定义一个美女
PettyGirl xiaohua = new Girl("校花");
AbstractPerson laoXu = new LaoXu(xiaohua);
laoXu.look();
}
}
运行结果如下:
--------美女的信息如下:---------------
校花---脸蛋很漂亮!
校花---身材非常棒!
校花---气质非常好!
老许看美女的程序开发完毕了,运行结果也正确。我们回头来想想这个程序有没有问 题,思考一下PettyGirl这个接口,这个接口是否做到了最优化设计?答案是没有,还可以对接口进行优化。
随着时代的发展老许的审美观也在变化,当你发现有一个女孩,脸蛋不怎么样,身材也一般般,但是气质非常好,我相信大部分人都会把这样的女孩叫美女,审美素质提升了,就产生了气质型美女,但是我们的接口却定义了美女必须是三者都具备,按照这个标准,气质型美女就不能算美女,那怎么办?可能你要说了,我重新扩展一个美女类,只实现greatTemperament方法,其他两个方法置空,什么都不写,不就可以了吗? 聪明,但是行不通!为什么呢?AbstracPerson依赖的是PettyGirl接口,它有三个方法,你只实现了两个方法,AbstractPerson的方法是不是要修改?我们上面的程序打印出来的信息少了两条,还让我怎么去辨别是不是美女呢?
我们把原PettyGirl接口拆分为两个接口,一种是外形美的美女IGoodBodyGirl,这类美女的特点就是脸蛋和身材极棒,超一流,但是没有审美素质,比如随地吐痰,文化程度比较低;另外一种是气质美的美女IGreatTemperamentGirl,谈吐和修养都非常高。我们把一个比较臃肿的接口拆分成了两个专门的接口,灵活性提高了,可维护性也增加了,不管以后是要外形美的美女还是气质美的美女都可以轻松地通过PettyGirl定义。
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() {
log.info(this.name + "---脸蛋很漂亮!");
}
//气质要好
public void greatTemperament() {
log.info(this.name + "---气质非常好!");
}
//身材要好
public void niceFigure() {
log.info(this.name + "---身材非常棒!");
}
}
通过这样的重构以后,不管以后是要气质美女还是要外形美女,都可以保持接口的稳定。当然,你可能要说了,以后可能审美观点再发生改变,只有脸蛋好看就是美女,那这个IGoodBody接口还是要修改的呀,确实是,但是设计是有限度的,不能无限地考虑未来的变更情况,否则就会陷入设计的泥潭中而不能自拔。
以上把一个臃肿的接口变更为两个独立的接口所依赖的原则就是接口隔离原则,让AbstractPerson依赖两个专用的接口比依赖一个综合的接口要灵活。接口是我们设计时对外提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。
迪米特法则
迪米特法则(Law of Demeter,LoD)也称为最少知识原则(Least Knowledge Principle,LKP),虽然名字不同,但描述的是同一个规则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的这么多public 方法,我就调用这么多,其他的我一概不关心。
我们现在来设计一个权限系统,Boss需要查看今天的日报,这时候,Boss找到TeamLeader去进行统计,TeamLeader再把结果告诉Boss,接下来来看代码:
日报类:
public class WorkContent{
}
TeamLeader类如下:
public class TeamLeader{
public void checkNumberOfWorkContents(List<WorkContent> list){
log.info("今天的日报数量" + list.size());
}
}
Boss类如下:
public class Boss {
public void command(TeamLeader teamLeader){
List<WorkContent> list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
list.add(new WorkContent());
}
teamLeader.checkNumberOfWorkContents(list);
}
}
测试代码如下:
public static void main(String[] args) {
Boss boss = new Boss();
TeamLeader teamLeader = new TeamLeader();
boss.command(teamLeader);
}
写到这里,功能都实现差不多了,代码看上去也没什么问题,根据迪米特法则,boss只想要结果,不需要和WorkContent直接交流,而TeamLeader统计需要引用Course对象。下面来进行改造:
public class TeamLeader{
public void checkNumberOfWorkContents(){
List<WorkContent> list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
list.add(new WorkContent());
}
log.info("今天的日报数量" + list.size());
}
}
public class Boss {
public void command(TeamLeader teamLeader){
teamLeader.checkNumberOfWorkContents();
}
}
学习软件设计原则,千万不能形成强迫症,碰到业务复杂的场景,我们要随机应变。