简介
Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use.
要求一个子系统的外部与其内部的通信必须通过一个同一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。
外观模式(Facade Pattern) 也称为 门面模式,该模式为客户端提供一个封装了子系统调用的高层对象,是客户端访问子系统的唯一通道。
其实,在我们日常的编码工作中,我们都会有意无意地大量使用 外观模式,但凡只要高层模块需要调度多个子系统(2个以上类对象),我们都会自觉地创建一个新类封装这些子系统,提供精简接口,让高层模块可以更加容易间接调用这些子系统的功能。
尤其是现阶段各种第三方 SDK,各种开源类库,很大概率都会使用 外观模式,尤其是你觉得调用越方便的,外观模式使用的一般更多。
外观模式 本质:封装交互,简化调用
主要解决
封装多个子系统代码,提供精简接口给到客户端使用,隔离客户端与子系统间的耦合。
优缺点
优点
- 减少系统相互依赖:使用 外观模式 隔离了客户与子系统,实现了客户类与子系统类的松耦合,降低原有系统的复杂度;
- 提高灵活性:客户端只需与 外观角色 交互,无须关心子系统的工作细节,提高了客户端使用的便捷性;同时子系统的变化对客户端并不可见(透明),将变化隔离开来,使得系统也更加灵活;
- 提高安全性:外观角色 可以控制客户端访问子系统粒度,因此可以根据权限控制开放相应子系统功能;
缺点
- 外观角色 太过臃肿(封装过多子系统),其与子系统(底层业务模块,经常变化)耦合过重,当业务变更时,只能通过修改本身代码调节功能,不符合设计模式 开闭原则;
使用场景
- 为一个复杂的模块或子系统提供一个简洁的供外界访问的接口;
- 希望提高子系统的独立性时
- 预防代码污染:当子系统由于不可避免的暂时原因导致可能存在 bug 或性能相关问题时,可以通过 外观模式 提供一个高层接口,隔离客户端与子系统的直接交互,方便后续问题修改;
模式讲解
首先来看下 外观模式 的通用 UML 类图:
从 UML 类图中,我们可以看到,外观模式 主要包含两种角色:
- 外观角色(Facade):也称 门面角色,系统对外的统一接口;
- 子系统角色(SubSystem):可以同时有一个或多个 SubSystem。每个 SubSytem 都不是一个单独的类,而是一个类的集合。SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言,Facade 只是另一个客户端而已(即 Facade 对 SubSystem 透明)。
下面是 外观模式 的通用代码:
class Client {
// 客户
public static void main(String[] args) {
Facade facade = new Facade();
facade.doA();
facade.doB();
facade.doC();
}
// 外观角色 Facade
static class Facade {
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();
}
}
// 子系统
static class SubSystemA {
public void doA() {
System.out.println("doing A stuff");
}
}
// 子系统
static class SubSystemB {
public void doB() {
System.out.println("doing B stuff");
}
}
// 子系统
static class SubSystemC {
public void doC() {
System.out.println("doing C stuff");
}
}
}
举个例子
假设小明的爷爷已经80岁了,一个人生活,他每天的习惯是:每次起床时都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;
如果我们不按 外观模式 组织代码,直接编写,则代码如下所示:
class Grandpa {
public static void main(String[] args){
Light light = new Light();
Television tv = new Television();
Aircondition ad = new Aircondition();
System.out.println("起床了");
light.on();
tv.on();
ad.on();
System.out.println("准备睡觉了");
light.off();
tv.off();
ad.off();
System.out.println("电器已全部关闭,可以安心睡觉了");
}
static class Light{
public void on(){
System.out.println("开灯");
}
public void off(){
System.out.println("关灯");
}
}
static class Television {
public void on(){
System.out.println("打开电视");
}
public void off(){
System.out.println("关电视");
}
}
static class Aircondition {
public void on(){
System.out.println("打开空调");
}
public void off(){
System.out.println("关空调");
}
}
}
上面的代码中,Grandpa
每次起床,都要自己去手动开灯,打开电视,打开空调,每次睡觉前,也要自己一个人手动去关灯,关电视,关空调,对于一个已经80岁的老人来说,太累了。
反思上面出现的问题,是因为灯,电视,空调3个子系统与爷爷(客户)高度耦合,爷爷(客户)严重依赖子系统。爷爷(客户)需要与每个电器(子系统)进行交互,爷爷(客户)操作太繁杂。
小明心想,不能老让爷爷这么劳累啊,于是小明就买了一个智能家具控制器(外观对象/统一接口),这样爷爷只需要一键就能控制灯、电视机、空调的打开和关闭。代码如下所示:
class Grandpa {
public static void main(String[] args) {
ControllerFacade facade = new ControllerFacade();
System.out.println("起床了");
facade.on();
System.out.println("准备睡觉了");
facade.off();
System.out.println("电器已全部关闭,可以安心睡觉了");
}
static class ControllerFacade {
private Light light = new Light();
private Television tv = new Television();
private Aircondition ad = new Aircondition();
public void on() {
this.light.on();
this.tv.on();
this.ad.on();
}
public void off() {
this.light.off();
this.tv.off();
this.ad.off();
}
}
static class Light {
public void on() {
System.out.println("开灯");
}
public void off() {
System.out.println("关灯");
}
}
static class Television {
public void on() {
System.out.println("打开电视");
}
public void off() {
System.out.println("关电视");
}
}
static class Aircondition {
public void on() {
System.out.println("打开空调");
}
public void off() {
System.out.println("关空调");
}
}
}
上面的代码中子系统代码不变,然后增加了一个外观类ControllerFacade
,最终客户(爷爷)的调用方式就精简许多了。
示例来源:外观模式(Facade Pattern) - 最易懂的设计模式解析