外观模式(Facade Pattern):又称为门面模式,为一组接口提供一个统一的入口。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色降低原有系统的复杂度,同时降低客户端类与子系统的耦合度。

外观模式

  • Facade(外观角色):外观角色中可以知道相关的多个子系统的功能和责任,客户端调用它的方法,它再传递给相应的子系统对象处理
  • SubSystem(子系统角色):子系统可以不是单独的类,而是类的集合,它实现子系统的功能,每个子系统都可以被客户端直接调用,或者被外观角色调用,对于子系统而言,外观角色也是一个客户端。


最近习惯了自己做饭,虽然做饭的过程很痛苦,可是看到自己做出来的美食后,还是很幸福很有成就感的。就拿自己做饭吃和去餐馆吃饭来举例,把餐馆看做外观角色,让它把买菜、切菜、炒菜、刷碗这些工作统一组织起来,我要做的就是告诉他要吃什么就行了,下面是示例代码:

SubSystem 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 买菜
public class BuyVegetable {
public void buy() {
LogUtils.i("买菜");
}
}

// 切菜
public class CutVegetable {
public void cut() {
LogUtils.i("切菜");
}
}

// 炒菜
public class CookVegetable {
public void cook() {
LogUtils.i("炒菜");
}
}

// 洗刷刷
public class WashDishes {
public void wash() {
LogUtils.i("洗刷刷");
}
}

Facade 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 餐馆
public class Restaurant {

private final BuyVegetable mBuyVegetable;
private final CutVegetable mCutVegetable;
private final CookVegetable mCookVegetable;
private final WashDishes mWashDishes;

public Restaurant() {
mBuyVegetable = new BuyVegetable();
mCutVegetable = new CutVegetable();
mCookVegetable = new CookVegetable();
mWashDishes = new WashDishes();
}

public void eat() {
mBuyVegetable.buy();
mCutVegetable.cut();
mCookVegetable.cook();
mWashDishes.wash();
}
}

Client 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 自己做饭,需要跟这些子系统交互
BuyVegetable buyVegetable = new BuyVegetable();
CutVegetable cutVegetable = new CutVegetable();
CookVegetable cookVegetable = new CookVegetable();
WashDishes washDishes = new WashDishes();
buyVegetable.buy();
cutVegetable.cut();
cookVegetable.cook();
washDishes.wash();

// 去餐馆吃饭,只需跟餐馆交互
Restaurant restaurant = new Restaurant();
restaurant.eat();

有了外观模式,需要交互的类就变成了一个,让它负责和业务类实现交互,简化负责的交互,降低系统的耦合度,但是在标准的外观模式中,如果需要增删改外观类交互的子系统类,就需要改动客户端源码,这样就违反了“开闭原则”,因此遇到此类情况需要引入抽象外观类进行优化,还以上面例子为基础:

AbstarctFacade 类:

1
2
3
public abstract class AbstractFacade {
public abstract void eat();
}

ConcreteFacade 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 有些不用切就可以直接做的饭,比如面,这就需要把切菜移除掉
public class NoodlesRestaurant extends AbstractFacade{

private final BuyVegetable mBuyVegetable;
private final CookVegetable mCookVegetable;
private final WashDishes mWashDishes;

public NoodlesRestaurant() {
mBuyVegetable = new BuyVegetable();
mCookVegetable = new CookVegetable();
mWashDishes = new WashDishes();
}

@Override
public void eat() {
mBuyVegetable.buy();
mCookVegetable.cook();
mWashDishes.wash();
}
}

Client 类:

1
2
AbstractFacade abstractFacade = new NoodlesRestaurant();
abstractFacade.eat();

优点

  • 屏蔽子系统,减少客户端所需交互的对象,简化调用
  • 降低客户端与子系统耦合,面对子系统变化,只需要调整外观类即可
  • 子系统间的修改不会相互影响

缺点

  • 不能很好限制客户端直接使用子系统类,对访问子系统类做过多限制则减少可变性和灵活性
  • 设计不当时,增加新的子系统需要修改外观类源码,违背开闭原则

使用场景

  • 需要访问一系列子系统完成业务需求
  • 客户端和多个子系统很高的耦合,使用外观模式解耦,提高子系统的独立性和可移植性
  • 层次化结构中,使用外观模式定义系统中每层的入口,层与层之间不直接产生联系,通过外观类建立联系,降低层之间的耦合