设计模式之门面模式详解
·
设计模式之门面模式详解
一、什么是门面模式
门面模式(Facade Pattern) 又叫外观模式,提供了一个统一的接口,用来访问子系统中的一群接口 。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构型模式。在我们日常编码中也在无意中大量的使用着门面模式,例如
ServiceA中调用其他多个Service的方法,然后再将ServiceA的方法暴露给Controller调用,ServiceA就相当于一个门面使得Controller能够间接的调用其他Service子系统,这也就是门面模式的一种应用。
二、门面模式的应用场景
-
子系统越来越复杂,增加门面模式提供简单接口
-
构建多层系统结构,利用门面对象作为每层的入口,简化层间调用
三、门面模式的角色组成
门面模式的通用类图:

- 外观角色(Facade): 也称门面角色,系统对外的统一接口;
- 子系统角色(
SubSystem): 可以同时有一个或多个SubSystem。每个SubSystem都不是一个单独的类,而是一个类的集合。SubSystem并不知道Facade 的存在,对于SubSystem而言,Facade只是另外一个客户端而已(即Facade对SubSystem是透明的)。
四、门面模式通用写法
我们先分别创建3个子系统的业务逻辑SubSystemA、SubSystemB、SubSystemC
/**
* 子系统角色 - SubSystemA
*
* @author zdp
* @date 2022/9/3 14:38
*/
public class SubSystemA {
public void doA() {
System.out.println("doing A stuff");
}
}
/**
* 子系统角色 - SubSystemB
*
* @author zdp
* @date 2022/9/3 14:38
*/
public class SubSystemB {
public void doB() {
System.out.println("doing B stuff");
}
}
/**
* 子系统角色 - SubSystemC
*
* @author zdp
* @date 2022/9/3 14:38
*/
public class SubSystemC {
public void doC() {
System.out.println("doing C stuff");
}
}
紧接着再创建一个外观角色类Facade :
/**
* 外观角色 Facade 相当于注入其他Service,封装其方法暴露给客户端
*
* @author zdp
* @date 2022/9/3 14:34
*/
public class Facade {
//相当于@Autowire注入
private SubSystemA a = new SubSystemA();
private SubSystemB b = new SubSystemB();
private SubSystemC c = new SubSystemC();
// 对外接口
public void doA() {
this.a.doA();
}
// 对外接口
public void doB() {
this.b.doB();
}
// 对外接口
public void doC() {
this.c.doC();
}
}
编写测试类
/**
* facade 通用写法
*
* @author zdp
* @date 2022/9/3 14:36
*/
class Test {
// 客户 (相当于Controller)
public static void main(String[] args) {
Facade facade = new Facade();
facade.doA();
facade.doB();
facade.doC();
}
}
五、门面模式在业务中的应用
需求:现有积分、支付、库存、物流系统,需要做一个积分兑换商城
- 创建商品类
/**
* 商品详情信息
*
* @author zdp
* @date 2022/9/3 14:52
*/
@Data
@Builder
public class GoodsInfo {
private String name;
private Integer integral;
private BigDecimal price;
}
- 积分系统
/**
* 积分系统
*
* @author zdp
* @date 2022/9/3 14:51
*/
@Slf4j
public class IntegralSystem {
/**
* 扣减积分
*
* @param info GoodsInfo
* @author zdp
* @date 2022/9/3 15:10
* @return result
*/
public boolean deductIntegral(GoodsInfo info){
log.info("商品【{}】扣减积分成功", info.getName());
return true;
}
}
- 库存系统
/**
* 库存系统
*
* @author zdp
* @date 2022/9/3 14:54
*/
@Slf4j
public class StockSystem {
public static int goodsStock = 2;
/**
* 扣减库存
*
* @param info GoodsInfo
* @author zdp
* @date 2022/9/3 15:07
* @return result
*/
public boolean deductStock(GoodsInfo info){
goodsStock -= 1;
if (goodsStock < 0) {
log.info("商品【{}】扣减库存失败,库存不足~", info.getName());
return false;
}
log.info("商品【{}】扣减库存成功~", info.getName());
return true;
}
}
- 支付系统
/**
* 支付系统
*
* @author zdp
* @date 2022/9/3 14:52
*/
@Slf4j
public class PaymentSystem {
StockSystem stock = new StockSystem();
IntegralSystem integral = new IntegralSystem();
/**
* 付款
*
* @param info GoodsInfo
* @author zdp
* @date 2022/9/3 15:08
* @return result
*/
public boolean pay(GoodsInfo info){
if (stock.deductStock(info)) {
integral.deductIntegral(info);
log.info("商品【{}】支付成功,准备发货~", info.getName());
return true;
}
return false;
}
}
- 物流系统
/**
* 物流系统
*
* @author zdp
* @date 2022/9/3 14:49
*/
@Slf4j
public class LogisticsSystem {
/**
* 对接物流系统,商品发货
*
* @param info goods info
* @author zdp
* @date 2022/9/3 14:57
* @return java.lang.String 物流单号
*/
public void deliverGoods(GoodsInfo info){
//对接物流系统...
log.info("商品【{}】,进入物流系统,单号: {}", info.getName(), new Date().getTime());
}
}
- 外观角色门面
/**
* 外观角色 Facade 商品兑换门面 整合了支付和物流系统,使得前端开发逻辑处理简单,不用再调用多个系统
*
* @author zdp
* @date 2022/9/3 15:15
*/
public class GoodsFacadeSystem {
PaymentSystem payment = new PaymentSystem();
LogisticsSystem logistics = new LogisticsSystem();
/**
* 商品兑换
*
* @param info GoodsInfo
* @author zdp
* @date 2022/9/3 15:16
* @return 商品兑换结果
*/
public boolean exchange(GoodsInfo info){
if (!payment.pay(info)) {
return false;
}
logistics.deliverGoods(info);
return true;
}
}
- 测试
public class Test {
public static void main(String[] args) throws Exception{
GoodsFacadeSystem facade = new GoodsFacadeSystem();
for (int i = 0; i < 3; i++) {
GoodsInfo info = GoodsInfo.builder()
.name("拖孩儿")
.integral(100)
.price(new BigDecimal("10.05"))
.build();
facade.exchange(info);
TimeUnit.MILLISECONDS.sleep(1);
}
}
}

六、门面模式优缺点
- 优点
- 简化了调用过程,无需深入了解子系统,以防给子系统带来风险
- 减少系统依赖,松散耦合
- 更好地划分访问层次,提高了安全性
- 遵循迪米特法则,即最少知道原则
- 缺点
- 当增加子系统和扩展子系统行为时,可能容易带来风险
- 不符合开闭原则
- 某些情况下可能违背单一职责原则
更多推荐


所有评论(0)