门面(外观)模式
简介
门面模式(Facade Pattern)又叫作外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构型设计模式。
通用模板
-
创建子系统角色类:可以同时有一个或多个SubSystem。每个SubSytem都不是一个单独的类,而是一个类的集合。SubSystem并不知道Facade的存在,对于SubSystem而言,Facade 只是另一个客户端而已(即Facade对SubSystem透明)。
// 子系统A public class SubSystemA {public void doA(){System.out.println("子系统A处理一些事情...");} }
// 子系统B public class SubSystemB {public void doB(){System.out.println("子系统B处理一些事情...");} }
-
创建门面角色类:是系统对外的统一接口。
// 门面类 public class Facade {private SubSystemA a = new SubSystemA();private SubSystemB b = new SubSystemB();// 对外接口public void doA(){a.doA();}// 对外接口public void doB(){b.doB();} }
模板测试
-
代码
public class Client {public static void main(String[] args) {Facade facade = new Facade();facade.doA();facade.doB();} }
-
结果
子系统A处理一些事情... 子系统B处理一些事情...
应用场景
在日常生活中,门面模式也是很常见的。比如,我们去医院就诊,很多医院都设置了导诊台,这个导诊台就好比一个门面。有了这个导诊台,我们全程就诊都不需要到处乱转,就诊路线变得非常清楚。再比如,现在中国就要全面进入小康社会,很多农村家家户户都建起了小别墅。那么,建别墅也是一项很复杂的工程。在以前,都是相互帮忙把房子建起来,但是建别墅一般要找一个承建方,负责设计、施工等。我们通常说的包工头其实就是一个门面,在施工过程中有任何需要协调对接的找包工头就可以了。 在软件系统中,门面模式适用于以下应用场景。
(1)为一个复杂的模块或子系统提供一个简洁的供外界访问的接口。
(2)希望提高子系统的独立性时。
(3)当子系统由于不可避免的暂时原因导致可能存在Bug或性能相关问题时,可以通过门面模式提供一个高层接口,隔离客户端与子系统的直接交互,预防代码污染。
优点
(1)简化了调用过程,不用深入了解子系统,以防给子系统带来风险。
(2)减少系统依赖,松散耦合。
(3)更好地划分访问层次,提高了安全性。
(4)遵循迪米特法则。
缺点
(1)当增加子系统和扩展子系统行为时,可能容易带来未知风险。
(2)不符合开闭原则。
(3)某些情况下,可能违背单一职责原则。
“生搬硬套”实战
场景描述
我们以注册公司为例,假设注册公司需要三步:
- 向工商局申请公司营业执照;
- 在银行开设账户;
- 在税务局开设纳税号。
现在各地政府都有政务大厅,再也不用去各个地方跑去问怎么处理,直接通过政务大厅就一次性办理了。
代码开发
-
创建子系统角色(这里指的是工商注册、银行开户、纳税登记)类
// 工商注册 public class AdminOfIndustry {public void register() {System.out.println("工商注册");} }
// 银行开户 public class Bank {public void openAccount() {System.out.println("银行开账户");} }
// 纳税登记 public class Taxation {public void applyTaxCode() {System.out.println("纳税登记");} }
- 创建门面角色(这里指的是政务大厅)类
public class Facade {private AdminOfIndustry adminOfIndustry = new AdminOfIndustry();private Bank bank = new Bank();private Taxation taxation = new Taxation();public void register(){adminOfIndustry.register();}public void openAccount(){bank.openAccount();}public void applyTaxCode(){taxation.applyTaxCode();} }
至此,我们就通过“生搬硬套”门面模式的模板设计出一套通过门面就可以处理所有子系统的业务了,接下来我们进行测试:
-
测试代码
public class Client {public static void main(String[] args) {Facade facade = new Facade();facade.register();facade.openAccount();facade.applyTaxCode();} }
-
结果
工商注册 银行开账户 纳税登记
总结
在日常编码工作中,我们都在有意无意地大量使用门面模式。但凡只要高层模块需要调度多个子系统(2个以上类对象),我们都会自觉地创建一个新类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用门面模式。大家觉得调用方便的,一般门面模式使用得更多。