一、外观模式基本介绍:
》定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口
》外观模式定义了一个高层接口,让系统更容易使用
》类型:结构型
》外观适用场景:
1)子系统越来越复杂,增加外观模式提供简单调用接口;
2)构建多层系统结构,利用外观对象作为每层的入口,简化层间调用;
》优点:
1)简化了调用过程,无需了解深入子系统,防止带来风险;
2)减少系统依赖、松散耦合;
3)更好的划分访问层次
4)符合迪米特法则,即最少知道法则
》缺点:
1)增加子系统、扩展子系统行为容易引入风险;
2)不符合开闭原则;
》改观相关的设计模式:
1)外观模式和中介者模式:外观模式关注外界和子系统之间的交互,而中介者模式关注的是内部子系统之间的交互;
2)外观模式和单例模式:通常可以把外观模式中的外观对象创建成单例对象
3)外观模式和抽象工厂模式:外观类可以通过抽象工厂模式创建不同的子系统对象
二、代码实践:
场景:
积分兑换礼品:简单分析该场景,假设要对外通过RPC 对外提供一个接口,供需要积分兑换的场景直接使用,传入某个礼品和积分,即可确定是否兑换
,以及如何兑换。积分兑换礼品简单需要如下几个子系统服务:积分库存校验(积分是不是够等)、积分支付(如果够的话,如何实现积分支付)、礼品物流配送服务(积分满足,并且用户已经支付相应积分,如何配送,我们以产生订单号为准)等几项服务。
如果假设我们只需要这几项服务,从上可以看到,几项服务之间的调用顺序要满足一定条件,也就是说积分兑换系统,包含几个子系统:积分校验系统、积分支付系统、物流配送系统等。为了对外使用简单、对外提供接口安全、用户不需要知道那么多等考虑,我们应该简化提供的接口的使用方法,最好一个方法实现。所以在此使用外观模式,通过封装一个积分兑换服务接口提供给其他业务使用者,该服务内部处理各种逻辑,让用户并不知道内部各个子系统是什么,以及子系统之间的关系,代码如下:
创建一个基本对象:礼物,三个子系统服务:积分校验、积分支付、物流配送;一个外观对象:积分兑换服务,提供一个兑换礼物的方法。
因为没有在spring框架中,所有的服务注入采用set 方法,如果是在spring框架中,可以采用其他如注解注入等的方式,这里只是重视设计模式的应用,所以都以核心关心点为重,简单起见。真实业务场景,具体的代码关心点肯定很多。但基本大体如此。
如之上描述,代码如下:
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
* 礼物积分
*/
public class PointsGift {
private String name;
public PointsGift(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class QulaifyService {
public boolean isAvailable(PointsGift pointsGift){
System.out.println("积分校验"+pointsGift.getName()+"积分通过,库存通过");
return true;
}
}
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class PointsPaymentService {
public boolean pay(PointsGift pointsGift){
//扣减积分
System.out.println("支付"+pointsGift.getName()+"积分成功");
return true;
}
}
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class ShippingService {
public String shipGift(PointsGift pointsGift){
//物流系统的对接逻辑
System.out.println(pointsGift.getName()+"进入物流系统");
String shippingOrderNo = "666";
return shippingOrderNo;
}
}
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class PointsGiftExchangeService {
private QulaifyService qulaifyService;
private PointsPaymentService pointsPaymentService;
private ShippingService shippingService;
public void giftExchange(PointsGift pointsGift){
if (qulaifyService.isAvailable(pointsGift)) {
//资格校验通过
if (pointsPaymentService.pay(pointsGift)){
//如果支付积分成功
String shippingOrderNo = shippingService.shipGift(pointsGift);
System.out.println("物流系统下单成功,订单号是"+shippingOrderNo);
}
}
}
public void setQulaifyService(QulaifyService qulaifyService) {
this.qulaifyService = qulaifyService;
}
public void setPointsPaymentService(PointsPaymentService pointsPaymentService) {
this.pointsPaymentService = pointsPaymentService;
}
public void setShippingService(ShippingService shippingService) {
this.shippingService = shippingService;
}
}
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class Test {
public static void main(String[] args) {
//假设要兑换的礼物是积分
PointsGift pointsGift = new PointsGift("T恤");
//调用积分兑换服务
PointsGiftExchangeService giftExchangeService = new PointsGiftExchangeService();
//因为这里没有spring框架,之前没有将该对象创建出来,所以在此可以看到如下
//几项服务的创建过程,事实是,我们这里模拟也可以通过在礼物交换服务中,初始化
//各项服务时,直接创建对象,此处即可省去,在此写出,只是为了顺便让大家理解
//外观模式的优点(可以通过UML类图发现这么写和简化后的差异
giftExchangeService.setQulaifyService(new QulaifyService());
giftExchangeService.setPointsPaymentService(new PointsPaymentService());
giftExchangeService.setShippingService(new ShippingService());
giftExchangeService.giftExchange(pointsGift);
}
}
运行结果如下:
"D:\Program Files\Java\jdk1.8.0_102\bin\java" "-javaagent:D:\InteliijIDea\IntelliJ IDEA 2017.1.4\lib\idea_rt.jar=50170:D:\InteliijIDea\IntelliJ IDEA 2017.1.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_102\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\rt.jar;C:\Users\Administrator\Desktop\gson-master\gson-master\DesignMode\target\classes" com.zxl.design.pattern.structural.facade.Test
积分校验T恤积分通过,库存通过
支付T恤积分成功
T恤进入物流系统
物流系统下单成功,订单号是666
Process finished with exit code 0
如上,我们成功演示了基本的外观模式的引用,看下类结构图
如上,调用关系 如上图,积分交换服务管理三个子系统之间的逻辑,只是对外提供一个接口,而业务层只需要调用该服务即可,实际上,上面这个类图显示test有和子系统之间的连线,不是我们想要的,而这个问题是因为我们没有使用spring的框架,
下面我们假设在交换服务中创建了各个子系统对象,这里就会看到更真实的外观模式的类结构图
下面优化下我们模拟的交换服务类和测试类代码
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class PointsGiftExchangeService {
private QulaifyService qulaifyService = new QulaifyService();
private PointsPaymentService pointsPaymentService = new PointsPaymentService();
private ShippingService shippingService = new ShippingService();
public void giftExchange(PointsGift pointsGift){
if (qulaifyService.isAvailable(pointsGift)) {
//资格校验通过
if (pointsPaymentService.pay(pointsGift)){
//如果支付积分成功
String shippingOrderNo = shippingService.shipGift(pointsGift);
System.out.println("物流系统下单成功,订单号是"+shippingOrderNo);
}
}
}
public void setQulaifyService(QulaifyService qulaifyService) {
this.qulaifyService = qulaifyService;
}
public void setPointsPaymentService(PointsPaymentService pointsPaymentService) {
this.pointsPaymentService = pointsPaymentService;
}
public void setShippingService(ShippingService shippingService) {
this.shippingService = shippingService;
}
}
package com.zxl.design.pattern.structural.facade;
/**
* Created by Administrator on 2019/7/7.
*/
public class Test {
public static void main(String[] args) {
//假设要兑换的礼物是积分
PointsGift pointsGift = new PointsGift("T恤");
//调用积分兑换服务
PointsGiftExchangeService giftExchangeService = new PointsGiftExchangeService();
//因为这里没有spring框架,之前没有将该对象创建出来,所以在此可以看到如下
//几项服务的创建过程,事实是,我们这里模拟也可以通过在礼物交换服务中,初始化
//各项服务时,直接创建对象,此处即可省去,在此写出,只是为了顺便让大家理解
//外观模式的优点(可以通过UML类图发现这么写和简化后的差异
// giftExchangeService.setQulaifyService(new QulaifyService());
// giftExchangeService.setPointsPaymentService(new PointsPaymentService());
// giftExchangeService.setShippingService(new ShippingService());
giftExchangeService.giftExchange(pointsGift);
}
}
类图如下:
如上,这才是一般的spring框架中最常见的标准的外观者模式的类图状态
针对上方再做分析,这里不符合开闭原则,如何更改呢?就是在交换服务中,添加一个抽象类,通过对交换服务的抽象,更符合开闭原则。在此不做具体描述。
三、外观模式的相关源码解析呢?
具体包括
jdbc对象中,封装了java原生 sql中的内容。
spring框架中的多个内容,比如Hibernate 框架、mybatis 框架中又对spring jdbc 中的接口进行了封装,具体为 比如不同参数的封装等等。
Tomcat中封装了 http中很多内容进行了封装,比如request requestReponse 中对facade 相关的内容。
还有其他spring等中大量使用到外观模式的地方,大家可自行去查看源码,进行分析。