3.1 场 景 问 题
3.1.1 生活中的示例
外观模式在现实生活中的示例很多,比如组装电脑,通常会有两种方案。
一个方案是去电子市场把自己需要的配件都买回来,然后自己组装,绝对DIY(Do It Yourself)。这个方案好是好,但是需要对各种配件都要比较熟悉,这样才能选择最合适的配件,而且还要考虑配件之间的兼容性,如图3.1所示。
图3.1 客户完全自己组装电脑
另外一个方案,就是到电子市场,找一家专业的装机公司,把具体的要求提出来,然后等着拿电脑就好了。当然价格会比自己全部DIY贵一些,但综合起来还算是个不错的选择,这也是大多数人的选择,如图3.2所示。
图3.2 找专业的装机公司组装电脑
这个专业的装机公司就相当于本章的主角——外观模式(Facade)。有了它,我们就不用自己去和众多卖配件的公司打交道,只需要跟装机公司交互就可以了,并将组装好的电脑返回给我们。
把上面的过程抽象一下,如果把电子市场看成是一个系统,而各个卖配件的公司看成是模块的话,就类似于出现了这样一种情况:客户端为了完成某个功能,需要去调用某个系统中的多个模块,把它们称为A模块、B模块和C模块。对于客户端而言,那就需要知道A、B、C这三个模块的功能,还需要知道如何组合这多个模块提供的功能来实现自己所需要的功能,非常麻烦。
要是有一个简单的方式能让客户端去实现相同的功能该多好啊,这样,客户端就不用跟系统中的多个模块交互,而且客户端也不需要知道那么多模块的细节功能了,实现这个功能的就是Facade。
3.1.2 代码生成的应用
考虑这样一个实际的应用:代码生成。
很多公司都有这样的应用工具,能根据配置生成代码。一般这种工具都是公司内部使用,较为专有的工具,生成的多是按照公司的开发结构来实现的常见的基础功能,比如增删改查。这样每次在开发实际应用的时候,就可以以很快的速度把基本的增删改查实现出来,然后把主要的精力都放在业务功能的实现上。
当然这里不可能去实现一个这样的代码生成工具,那需要整本书的内容,这里仅用它来说明外观模式。
假设使用代码生成出来的每个模块都具有基本的三层架构,分为表现层、逻辑层和数据层,那么代码生成工具里面就应该有相应的代码生成处理模块。
另外,代码生成工具自身还需要一个配置管理的模块,通过配置来告诉代码生成工具,每个模块究竟需要生成哪些层的代码。比如,通过配置来描述,是只需要生成表现层代码呢,还是三层都生成。具体的模块示意如图3.3所示。
图3.3 代码生成工具的模块示意图
那么现在客户端需要使用这个代码生成工具来生成需要的基础代码,该如何实现呢?
3.1.3 不用模式的解决方案
有朋友会想,开发一个这样的工具或许会比较麻烦,但是使用一下,应该不难吧,直接调用不就可以了。
在示范客户端之前,先来把工具模拟示范出来。为了简单,每个模块就写一个类,而且每个类只是实现一个功能,仅仅做一个示范。
(1)先看看描述配置的数据Model。示例代码如下:
(2)接下来看看配置管理的实现示意。示例代码如下:
(3)再来看看各个生成代码的模块。在示意中,它们的实现类似,就是获取配置文件的内容,然后按照配置来生成相应的代码。
先来看生成表现层的示意实现。示例代码如下:
再来看生成逻辑层的示意实现。示例代码如下:
下面是生成数据层的示意实现。示例代码如下:
(4)此时的客户端实现,就应该自行去调用这多个模块了。示例代码如下:
运行结果如下:
3.1.4 有何问题
仔细查看上面的实现,会发现其中有一个问题:那就是客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块交互。
这对于客户端而言,是个麻烦,使得客户端不能简单地使用生成代码的功能。而且,如果其中的某个模块发生了变化,还可能会引起客户端也要随着变化。
那么如何实现,才能让子系统外部的客户端在使用子系统的时候,既能简单地使用这些子系统内部的模块功能,而又不用客户端去与子系统内部的多个模块交互呢?