意图 外观模式(Facade Pattern
):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。
模式结构 外观模式包含如下角色:
Facade: 外观角色
SubSystem:子系统角色
现实世界的例子 金矿如何运作? “好吧,矿工们到那里去挖金子吧!” 你说。 这就是你所相信的,因为你使用的是 goldmine 在外部提供的一个简单界面,在内部它必须做很多事情才能实现。 这个复杂子系统的简单接口就是外观。
程序化示例 让我们以上面的金矿为例。这里我们有矮人矿工的等级制度。首先有一个基类DwarvenMineWorker
:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public abstract class DwarvenMineWorker { public abstract String name () ; public abstract void work () ; public void wakeUp () { System.out.println(name() + " wakes up." ); } public void goToMine () { System.out.println(name() + " goes to the mine." ); } public void goHome () { System.out.println(name() + " goes home." ); } public void goToSleep () { System.out.println(name() + " goes to sleep." ); } enum Action { WAKE_UP, GO_TO_MINE, GO_HOME, GO_TO_SLEEP, WORK } public void action (Action... action) { Arrays.stream(action).forEach(this ::action); } private void action (Action action) { switch (action) { case WAKE_UP: wakeUp(); break ; case GO_TO_MINE: goToMine(); break ; case GO_HOME: goHome(); break ; case GO_TO_SLEEP: goToSleep(); break ; case WORK: work(); break ; default : System.out.println("Undefine action!" ); } } }
然后我们有具体的矮人类矮人隧道挖掘者DwarvenTunnelDigger
,矮人掘金者DwarvenGoldDigger
和矮人推车操作员DwarvenCartOperator
:
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 28 29 30 31 32 33 34 35 36 37 38 public class DwarvenTunnelDigger extends DwarvenMineWorker { @Override public String name () { return "DwarvenTunnelDigger" ; } @Override public void work () { System.out.println(this .name() + " creates another promising tunnel." ); } } public class DwarvenGoldDigger extends DwarvenMineWorker { @Override public String name () { return "DwarvenGoldDigger" ; } @Override public void work () { System.out.println(this .name() + " digs for gold." ); } } public class DwarvenCartOperator extends DwarvenMineWorker { @Override public String name () { return "DwarvenCartOperator" ; } @Override public void work () { System.out.println(this .name() + " moves gold chunks out of the mine." ); } }
为了操作所有这些金矿工人,我们有矮人金矿门面DwarvenGoldmineFacade
:
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 public class DwarvenGoldmineFacade { private final List<DwarvenMineWorker> workers; public DwarvenGoldmineFacade () { workers = Arrays.asList(new DwarvenTunnelDigger(), new DwarvenGoldDigger(), new DwarvenCartOperator()); } public void startNewDay () { makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); } public void digOutGold () { makeActions(workers, DwarvenMineWorker.Action.WORK); } public void endDay () { makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); } private static void makeActions (List<DwarvenMineWorker> workers, DwarvenMineWorker.Action... actions) { workers.forEach(worker -> worker.action(actions)); } }
现在让我们使用门面:
1 2 3 4 var facade = new DwarvenGoldmineFacade();facade.startNewDay(); facade.digOutGold(); facade.endDay();
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 DwarvenTunnelDigger wakes up. DwarvenTunnelDigger goes to the mine. DwarvenGoldDigger wakes up. DwarvenGoldDigger goes to the mine. DwarvenCartOperator wakes up. DwarvenCartOperator goes to the mine. DwarvenTunnelDigger creates another promising tunnel. DwarvenGoldDigger digs for gold. DwarvenCartOperator moves gold chunks out of the mine. DwarvenTunnelDigger goes home. DwarvenTunnelDigger goes to sleep. DwarvenGoldDigger goes home. DwarvenGoldDigger goes to sleep. DwarvenCartOperator goes home. DwarvenCartOperator goes to sleep.
外观模式适合应用场景
子系统通常会随着时间的推进变得越来越复杂。 即便是应用了设计模式, 通常你也会创建更多的类。 尽管在多种情形中子系统可能是更灵活或易于复用的, 但其所需的配置和样板代码数量将会增长得更快。 为了解决这个问题, 外观将会提供指向子系统中最常用功能的快捷方式, 能够满足客户端的大部分需求。
客户端和抽象的实现类之间存在许多依赖关系。引入外观将子系统与客户端和其他子系统解耦,从而提高子系统的独立性和可移植性。
如果需要将子系统组织为多层结构,可以使用外观。创建外观来定义子系统中各层次的入口。 你可以要求子系统仅使用外观来进行交互, 以减少子系统之间的耦合。让我们回到视频转换框架的例子。 该框架可以拆分为两个层次: 音频相关和视频相关。 你可以为每个层次创建一个外观, 然后要求各层的类必须通过这些外观进行交互。 这种方式看上去与中介者模式非常相似。
模式分析 根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口。 -外观模式也是“迪米特法则”的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。 - 外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。 -外观模式的目的在于降低系统的复杂程度。 -外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。
总结
在外观模式中,外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。
外观模式包含两个角色:外观角色是在客户端直接调用的角色,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理;在软件系统中可以同时有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能。
外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。
外观模式主要优点在于对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易,它实现了子系统与客户之间的松耦合关系,并降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程;其缺点在于不能很好地限制客户使用子系统类,而且在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
外观模式适用情况包括:要为一个复杂子系统提供一个简单接口;客户程序与多个子系统之间存在很大的依赖性;在层次化结构中,需要定义系统中每一层的入口,使得层与层之间不直接产生联系。