1.概述
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
举个现实生活中的例子:在大陆去大型医院看病
下面用一个示意图来表示这个过程:
实现上面这种流程的简易代码实现:
package cn.xnn;
/**
* 类(接口)描述:挂号类
* @author xnn
* 2018年11月25日下午9:48:48
*/
public class Register {
public void Regist() {
System.out.println("挂号成功");
}
}
package cn.xnn;
/**
* 类(接口)描述:门诊类
* @author xnn
* 2018年11月25日下午9:51:29
*/
public class OutpatientService {
public void Diagnosis() {
System.out.println("医生开始诊断了");
}
}
package cn.xnn;
/**
* 类(接口)描述:化验类
* @author xnn
* 2018年11月25日下午10:20:55
*/
public class Test {
public void test() {
System.out.println("去化验");
}
}
package cn.xnn;
/**
* 类(接口)描述:划价类
* @author xnn
* 2018年11月25日下午9:53:10
*/
public class Pricing {
public void pay() {
System.out.println("诊断完毕,开始划价缴费");
}
}
package cn.xnn;
/**
* 类(接口)描述:抓药类
* @author xnn
* 2018年11月25日下午9:54:24
*/
public class Prescription {
public void fetch () {
System.out.println("划价缴费完毕,可以抓药了");
}
}
package cn.xnn;
/**
* 类(接口)描述:患者类
* @author xnn
* 2018年11月25日下午9:55:48
*/
public class Patient {
private static Register register =new Register();
private static OutpatientService outpatientService=new OutpatientService();
private static Prescription prescription=new Prescription();
private static Pricing pricing=new Pricing();
private static Test test = new Test();
public static void main(String[] args) {
//挂号
register.Regist();
//去门诊看病
outpatientService.Diagnosis();
//化验
test.test();
//划价缴费
pricing.pay();
//取药
prescription.fetch();
}
}
类图结构:
从系统图中可以看到,病人看病需要挂号、门诊诊断、化验、划价、取药等过程,并且所有的这些过程都需要患者(客户端)去亲力亲为访问医院这个系统(软件系统)。显然,从图上就可以感觉到,相互间调用太麻烦,耦合性也很大;
从类图中也可以感觉到,patient这个类引用的对象太多了,即客户端和系统内部接触太多,耦合太大。
解决这种不便的一个方式就是引入外观(门面)模式,具体设计如下(还是以上面的医院系统为例):
在其中间加一个接待员(即后面要说的门面类)的角色,这样整个系统看起来就没有那么乱了。
代码体现(在上面的基础上引入一个接待员类,并且客户端类也要做一些改动):
package cn.xnn;
/**
* 类(接口)描述:接待员类
* @author xnn
* 2018年11月25日下午10:09:10
*/
public class Receptionist {
private Register register;
private OutpatientService outpatientService;
private Prescription prescription;
private Pricing pricing;
private Test test;
/**
* 在构造门面类时 创建这些相关的组件 ( 相当于把这些引用放到门面类中去,而不是让客户端直接引用)
*/
public Receptionist() {
register = new Register();
outpatientService=new OutpatientService();
prescription=new Prescription();
pricing=new Pricing();
test = new Test();
}
//看小病
public void DiagnosisLightIllness() {
//挂号
register.Regist();
//划价缴费
pricing.pay();
//取药
prescription.fetch();
}
//看大病
public void DiagnosisCriticallyIllness() {
//问诊
register.Regist();
//化验
test.test();
//划价缴费
pricing.pay();
//取药
prescription.fetch();
}
}
package cn.xnn;
/**
* 类(接口)描述:
* @author xnn
* 2018年11月25日下午10:27:39
*/
public class NewPatient {
//引入门面类
private static Receptionist receptionist =new Receptionist();
public static void main(String[] args) {
//看小病
receptionist.DiagnosisLightIllness();
System.out.println("----------------");
//看大病
receptionist.DiagnosisCriticallyIllness();
}
}
类图:
从改动后的代码看,客户端只需要引入一个门面类对象的引用即可,减少耦合,并且客户端代码量也减少很多。
2、模式中的角色
3、模式的优缺点
优点:
1、减少系统相互依赖;
2、符合迪米特法则(客户端只“认识”门面类就好)
3、提高灵活性;
4、提高了安全性
缺点:
不符合开闭原则(对扩展开放,对修改关闭。即系统若想增加一些功能,则我们可以通过扩展的方式,去实现这些想要的功能,而不必去修改原有的代码。),若系统运行一段时间后,发现功能不全,需要增加一个子系统去实现另外的一个功能,这个时候,你不仅需新增一个子系统的代码(对扩展开放,符合开闭原则。)但若想还甩这个门面模式的话,就不得不修改门面角色这个类(违背了对修改关闭这一原则);但是可以通过引入抽象外观类在一定程度上解决该问题,假若系统中又增加了一个新的子系统,我们可以在起初设计的时候就先写一个抽象外观类,然后写该抽象类的另一个具体实现类,把新的子系统功能封装进去。就上面的系统来说,若又增加了个报销子系统,我就可以再增加抽象外观类的子类,把报销的功能加进去,这样就符合了开——闭原则了。
4、模式的应用场景