外观模式
外观模式又称为门面模式是一种比较简单但却十分常用的设计模式,它的定义是:
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式通过提供一个外观类,来简化客户端与子系统的交互,为复杂的子系统提供一个统一的入口。
简单概述
举一个简单的例子,一日三餐是我们每个人的必须,而怎么吃我们可以有两种不同的选择,一是自己买菜烹饪,二是直接去饭店吃饭。这两种的区别相信大家都有感触,第一种我们自己烹饪,首先我们要去市场买菜,买调味料,甚至是锅碗瓢盆(单身狗的悲哀-_-||),然后再回家点火炒菜,如果一切顺利的话,你才有可能吃上一顿,填饱肚子。但是选择第二种就简单很多了,我们只要去饭店直接点菜,然后稍等一会就能吃上一顿美餐。在这里,买菜、买调味料、烹饪等都是子系统的一个个方法,而饭店就是外观类,选择第一种意味着作为客户端的我们需要与子系统进行复杂的交互,而第二种客户端仅仅与外观类进行一次交互即能得到同样的期望。如下图:

在软件开发中,当我们需要完成一个较为复杂的功能时,往往需要与多个子系统进行交互,当子系统比较简单时,我们尚且能正常的维护使用,但是当子系统过于复杂时,会导致代码逻辑混乱,不仅是对开发人员不方便,也会使得系统维护的难度大大提高。这种时候我们需要一个外观类来将复杂的业务逻辑进行封装,只提供一个简单的public方法供客户端调用。对于客户端来说,他并不需要知道复杂的业务逻辑,只需提供必要的参数,就能得到想要的结果。即便业务逻辑发生改动,我们也仅需修改外观类中与子系统交互的逻辑,而不影响客户端代码。
图示
外观模式没有一个统一的UML图,更多的时候使用如下示意图来表示外观模式。
代码示例
我们先创建几个子系统的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class SubSystem01 {
public void method01(){ System.out.println("子系统01的方法01"); } }
public class SubSystem02 {
public void method02(){ System.out.println("子系统02的方法02"); } }
public class SubSystem03 {
public void method03(){ System.out.println("子系统03的方法03"); } }
|
每个子系统各自都含有一个自己的方法,当客户端需要依次使用这些方法时,我们需要依次创建子系统的实例再调用它。
1 2 3 4 5 6 7 8 9 10 11 12
| public class Client {
public static void main(String[] args){ SubSystem01 subSystem01 = new SubSystem01(); SubSystem02 subSystem02 = new SubSystem02(); SubSystem03 subSystem03 = new SubSystem03(); subSystem01.method01(); subSystem02.method02(); subSystem03.method03(); } }
|
这种情况下,我们需要牢记各个子系统方法调用的时机以及结果,任何一步错了都将导致系统的崩溃。此时如果我们创建一个外观类,并将所有的逻辑操作都交于外观类处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class FacadeClass { private SubSystem01 system01; private SubSystem02 system02; private SubSystem03 system03;
public FacadeClass() { system01 = new SubSystem01(); system02 = new SubSystem02(); system03 = new SubSystem03(); }
public void method(){ system01.method01(); system02.method02(); system03.method03(); } }
|
此时客户端所需做的仅需创建一个外观类,并调用它提供的一个public方法即可
1 2 3 4 5 6 7
| public class Client {
public static void main(String[] args){ FacadeClass facadeClass = new FacadeClass(); facadeClass.method(); } }
|
通过使用外观模式,我们实现了程序的高内聚,低耦合,将与子系统的交互都交于外观类,减少了客户端与子系统的耦合,这样也就提高了系统的灵活性,不管子系统如何变化,只要外观类没有变化,都不会影响程序的运行。
抽象外观模式
上面所说的外观模式其实有一个问题,它不符合开闭原则,当子系统的调用逻辑发生改变,例如我们要在SubSystem02.method02()与SubSystem03.method03()之间增加一个新的子系统方法SubSystem04.method04(),此时我们就需要去修改FacadeClass 类的方法,这不符合对拓展开放,对修改关闭的原则。
此时我们可以通过增加一个抽象外观类来增加系统的灵活性,面向抽象开发也更符合依赖倒置的原则,当我们需要修改外观类的逻辑时,我们可以通过增加一个外观类来解决,客户端在使用时再决定使用哪一个外观类。
抽象外观类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public abstract class AbstractFacade{ protected SubSystem01 system01; protected SubSystem02 system02; protected SubSystem03 system03;
public AbstractFacade() { system01 = new SubSystem01(); system02 = new SubSystem02(); system03 = new SubSystem03(); }
public abstract void method(); }
|
这里可以看到,我在抽象外观类里初始化了一些子系统,它的继承类也就可以直接使用这些子系统
两个外观类
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
| public class FacadeClass01 extends AbstractFacade {
public FacadeClass01() { super(); }
@Override public void method() { system01.method01(); system02.method02(); system03.method03(); } }
public class FacadeClass02 extends AbstractFacade { SubSystem04 system04;
public FacadeClass02() { super(); system04 = new SubSystem04(); }
@Override public void method() { system01.method01(); system02.method02(); system04.method04(); system03.method03(); } }
|
客户端使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Client {
public static void main(String[] args){ AbstractFacade facade; facade = new FacadeClass01(); facade.method();
System.out.println("---------------------------------");
facade = new FacadeClass02(); facade.method(); } }
|
系统运行结果
1 2 3 4 5 6 7 8
| 子系统01的方法01 子系统02的方法02 子系统03的方法03 --------------------------------- 子系统01的方法01 子系统02的方法02 子系统04的method04方法 子系统03的方法03
|
外观模式的优缺点
优点
- 减少了客户端需要连接的子系统个数,简化客户端操作的复杂性
- 实现低耦合,高内聚
- 提高了安全性,通过外观类限制客户端对子系统随意的访问,只能调用特定的子系统来完成操作
缺点
- 限制了客户端对子系统的访问,也就意味着降低了系统的灵活性
- 如果系统设计不当,可能破坏设计模式的开闭原则,子系统改变而需修改外观类的源码
外观模式是我们实际开发中十分常用的设计模式,例如工具类的封装,我们常常会将一些比较常用的处理逻辑封装成一个工具类,客户端直接调用工具类提供的公共方法,而不需考虑方法的内部实现,使开发逻辑更为清晰。在实际使用中,我们还是要根据实际情况来选择合适的设计模式。
源码:https://github.com/lichenming0516/DesignPattern