本小节我们要学习的设计模式叫做外观模式,也叫做门面模式 Facade。想象一下,我们系统随着时间的推移,系统复杂性、类之间的相互调用会变得越来越多,相比较客户角度而言,客户往往关注的是某个单一接口 API,而不会关心该 API 内部的复杂性或者内部子系统是如何运作的。
举个栗子,我们都玩过射击类游戏,游戏玩家对战的时候,需要进行射击操作,而射击牵扯到一连串的动作,比如:上子弹、瞄准、发射子弹、掉血、加分等等一系列动作,这些动作我们可以理解为各个子系统的某个接口 API,比如上子弹、发射子弹可能是武器子系统的 API,掉血、加分可能是用户子系统的 API,客户角度需要调用的接口其实只有一个,那就是射击 API,这就是具体的门面接口,门面内部的各个子系统的动作对客户是透明的,这种客户只需要调用门面接口 API 就实现了一连串内部动作(上子弹、瞄准、发射子弹、掉血、加分等)的模式其实就叫做外观模式,也叫做门面模式。
外观模式的定义是:为各个子系统的一组接口提供一致的调用窗口或门面,使得子系统更容易使用,使得复杂的子系统与客户端分离解耦。
下面用一个简单的例子来说明下使用外观模式和不使用外观模式下系统设计的差别,继续看吧。
这里还是以上面的射击游戏为例,先来看下不使用外观模式时候的类图设计:
这里代码比较简单,我们直接列出武器系统和用户系统的示例代码:
/**
* @Desc 武器子系统
* @Author chaozhou
*/
public class FireSystem {
public void fire() {
System.out.println("开火....");
}
public void useBullet() {
System.out.println("上子弹....");
}
}
...
/**
* @Desc 用户子系统
* @Author chaozhou
*/
public class UserSystem {
public void loseBlood() {
System.out.println("掉血...");
}
public void addScore() {
System.out.println("得分...");
}
}
测试类 Client 角色如下:
FireSystem fireSystem = new FireSystem();
UserSystem userSystem = new UserSystem();
fireSystem.useBullet(); // 上子弹
fireSystem.fire(); // 开火
userSystem.loseBlood(); // 掉血
userSystem.addScore(); // 加分
测试结果如下:
上子弹… 开火… 掉血… 得分…
上面不使用外观模式时,可以看到客户端需要自己去直接调用各个子系统 API,系统模块多的时候对客户端十分不友好,下面我们看下使用外观模式如何解决这种问题,外观模式的类图设计如下:
这里我们引入 Facade 角色,该角色内部包含各个子系统的被委托的对象,客户端的所有请求经过 Facade 角色中转,简化了客户端操作的复杂性,Facade 代码示例如下:
/**
* @Desc Facade 角色
* @Author chaozhou
*/
public class Facade {
// 被委托的对象
private FireSystem fireSystem;
private UserSystem userSystem;
public Facade(FireSystem fireSystem, UserSystem userSystem) {
this.fireSystem = fireSystem;
this.userSystem = userSystem;
}
// 模拟射击的门面接口 API
public void shooting() {
fireSystem.useBullet(); // 上子弹
fireSystem.fire(); // 开火
userSystem.loseBlood(); // 敌人掉血
userSystem.addScore(); // 自己加分
}
}
测试 Client 调整如下:
FireSystem fireSystem = new FireSystem();
UserSystem userSystem = new UserSystem();
Facade facade = new Facade(fireSystem, userSystem);
facade.shooting(); // 射击
结果输出如下:
上子弹… 开火… 掉血… 得分…
可以看出,门面模式下,客户端接口调用的复杂性有所降低,并且内部系统和客户端之间解耦,使用门面模式下的“接待员”接口即可完成功能操作。
外观模式的一般类图如上所示,包含的角色列举如下:
外观模式优点:
外观模式缺点:
这节我们介绍了什么是外观模式,以及外观模式的代码示例,总结下外观模式的特点及本节内容如下:
外观模式学完之后,大家可以类比下 Adapter 模式、Bridge 模式以及 Decorator 模式,比较下这几种模式的侧重点与不同