当你翻看一本新书的时候,总是会首先翻到书的目录页,通过目录,让你知道书的主要内容是什么,每一章的内容是从哪一页开始。对的,目录就像是一本书的窗口。本文要介绍的外观模式或者叫做门面模式,正如书的目录一样,往往充当着一个或者多个子系统窗口的角色。
1.外观模式
外观模式(Facade Pattern) 为复杂的子系统定义了一个统一的、高层的接口,以便于客户端方便地使用。
外关模式的重点是:掩盖了复杂系统的实现,通过外观层,客户端通常传入上下文Context(持有若干全局参数)等,就可以实现复杂的逻辑,那么对于客户来说,好像实现这个逻辑很简单似的。外观模式的UML类图:
又上图可见,客户端没有直接调用子系统接口,而是通过Facade接口间接调用,而Facade相当于子系统的代理,将子系统复杂的逻辑用简单的接口呈现给客户端。外观模式通常用于有着多个复杂子系统,而调用方希望通过一个简单的接口就能实现复杂业务逻辑的场景,尤其是多个子系统协同操作才能完成一项业务的情况。
2.代码实现
启动汽车时,司机只需要插入启动钥匙,转一下钥匙就可以轻易将汽车启动(当然,汽车档位和手刹要正确),看似简单的操作,其背后有一系列部件配合运行,才将汽车顺利启动,这些部件组成了汽车的启动系统,我们不妨将启动系统各个机械装置拆分来看,当作一个个子系统,为了简化,大致的启动汽车的原理假设简化成这样子:司机转动钥匙->点火系统通电->启动机启动->带动发动机启动->汽车启动成功。当然,实际要比这个复杂很多。
子系统一:蓄电池,给整个系统通电, 随机实数模拟当前电池电量百分率
/** * 汽车启动系统: 蓄电池 */ class StorageBattery { /** 电量百分比 */ private double energy; private static Random rand = new Random(47); public StorageBattery() { energy = rand.nextDouble(); //随机电量,0-1 } /** 电池容量是否足够启动 */ public boolean hasEnoughenergy() { return energy >= 0.1; } }
子系统二:点火开关
/** * 汽车启动系统:点火开关 */ class IgnitionLock { public void ignite() { System.out.println("点火开关开启"); } }
子系统三:启动机
/** * 汽车启动系统:启动机 */ class Starter { public void work() { System.out.println("启动机工作"); } }
子系统四:发动机
/** * 汽车启动系统:发动机 */ class Engine { public void run() { System.out.println("发动机运转"); System.out.println("汽车启动成功"); } }
外观类,就相当于司机插入钥匙的操作,为司机提供简便的启动接口start(),而不需要关心子系统的复杂运转。司机只需调用start(),汽车就可以启动
/** * 外观类 */ class Facade { private StorageBattery battery; private IgnitionLock ignitionLock; private Starter starter; private Engine engine; public Facade() { battery = new StorageBattery(); ignitionLock = new IgnitionLock(); starter = new Starter(); engine = new Engine(); } /** * 提供给客户端的统一、简洁的接口 */ public void startCar() { // 启动前提是汽车蓄电池有电 if (battery.hasEnoughenergy()) { //点火开关开启 ignitionLock.ignite(); //启动机开启 starter.work(); //发动机运转 engine.run(); } } }
司机调用外观对象接口
public class FacadeDemo { public static void main(String[] args) { Facade facade = new Facade(); facade.startCar(); } }
result:
点火开关开启
启动机工作
发动机运转
汽车启动成功
3. 总结
外观模式的核心思想是将复杂的子系统掩盖,提供一个简洁的接口给客户端调用。很多人认为,适配器模式就是包装一个类,外观模式就是包装了多个类,这是不对的,因为从核心思想来看,适配器模式是将现有的服务器接口,转换为新的的客户端调用接口,强调新事物为了重用老事物而进行的适配。外观模式是在客户调用之前将子系统包装起来;而适配器模式则是在服务器和客户端调用都已经存在但是客户端调用的方法并非服务器现有接口的情况下,为了能让客户端顺利调用到系统而进行的适配。适配器和外观模式两者的区别绝非类的数量。