设计模式讲解 — 外观模式(1)

场景问题

生活中的示例

外观模式在现实生活中的示例很多,比如:组装电脑,通常会有两种方案

  • 一个方案是去电子市场把自己需要的配件都买回来,然后自己组装,绝对DIY(Do It Yourself)。这个方案好是好,但是需要对各种配件都要比较熟悉,这样才能选择最合适的配件,而且还要考虑配件之间的兼容性。如图所示:
    这里写图片描述
  • 另外一个方案,就是到电子市场,找一家专业装机的公司,把具体的要求一讲,然后就等着拿电脑就好了。当然价格会比自己全部DIY贵一些,但综合起来这还算是个不错的选择,估计也是大多数人的选择。如图所示:
    这里写图片描述

这个专业的装机公司就相当于本章的主角——Facade。有了它,我们就不用自己去跟众多卖配件的公司打交道,只需要跟装机公司交互就可以了,由装机公司去跟各个卖配件的公司交互,并组装好电脑返回给我们。

把上面的过程抽象一下,如果把电子市场看成是一个系统,而各个卖配件的公司看成是模块的话,就类似于出现了这样一种情况:客户端为了完成某个功能,需要去调用某个系统中的多个模块,把它们称为是A模块、B模块和C模块吧,对于客户端而言,那就需要知道A、B、C这三个模块的功能,还需要知道如何组合这多个模块提供的功能来实现自己所需要的功能,非常麻烦。

要是有一个简单的方式能让客户端去实现相同的功能多好啊,这样,客户端就不用跟系统中的多个模块交互,而且客户端也不需要知道那么多模块的细节功能了,实现这个功能的就是Facade

代码生成的应用

考虑这样一个实际的应用:代码生成(用来说明外观模式)。

假设使用代码生成出来的每个模块都具有基本的三层架构,分为:表现层、逻辑层和数据层,那么代码生成工具里面就应该有相应的代码生成处理模块。

另外,代码生成工具自身还需要一个配置管理的模块,通过配置来告诉代码生成工具,每个模块究竟需要生成哪些层的代码,比如:通过配置来描述,是只需要生成表现层代码呢,还是三层都生成。具体的模块示意如图所示:
这里写图片描述

不用模式的解决方案

在示范客户端之前,先来把工具模拟示范出来,为了简单,每个模块就写一个类,而且每个类只是实现一个功能,仅仅做一个示范。

源码地址:https://gitee.com/liupeifeng3514/design_pattern

(1)先看看描述配置的数据Model,示例代码如下:

/**
 * 示意配置描述的数据Model,真实的配置数据会很多
 */
public class ConfigModel {
    /**
     * 是否需要生成表现层,默认是true
     */
    private boolean needGenPresentation = true;
    /**
     * 是否需要生成逻辑层,默认是true
     */
    private boolean needGenBusiness = true;
    /**
     * 是否需要生成DAO,默认是true
     */
    private boolean needGenDAO = true;

    //  set,get方法
}

(2)接下来看看配置管理的实现示意,示例代码如下:

/**
 * 示意配置管理,就是负责读取配置文件,
 * 并把配置文件的内容设置到配置Model中去,是个单例
 */
public class ConfigManager {
    private static ConfigManager manager = null;
    private static ConfigModel cm = null;
    private ConfigManager(){

    }
    public static ConfigManager getInstance(){
        if(manager == null){
            manager = new ConfigManager();
            cm = new ConfigModel();
            //读取配置文件,把值设置到ConfigModel中去
        }
        return manager;
    }
    /**
     * 获取配置的数据
     * @return 配置的数据
     */
    public ConfigModel getConfigData(){
        return cm;
    }
}

(3)接下来就来看看各个生成代码的模块,在示意中,它们的实现类似,就是获取配置文件的内容,然后按照配置来生成相应的代码。分别看看它们的示意实现:

/**
 * 示意生成表现层的模块
 */
public class Presentation {
    public void generate() {
        // 1:从配置管理里面获取相应的配置信息
        ConfigModel cm = ConfigManager.getInstance().getConfigData();
        if (cm.isNeedGenPresentation()) {
            // 2:按照要求去生成相应的代码,并保存成文件
            System.out.println("正在生成表现层代码文件");
        }
    }
}
/**
 * 示意生成逻辑层的模块
 */
public class Business {
    public void generate() {
        // 1:从配置管理里面获取相应的配置信息
        ConfigModel cm = ConfigManager.getInstance().getConfigData();
        if (cm.isNeedGenBusiness()) {
            // 2:按照要求去生成相应的代码,并保存成文件
            System.out.println("正在生成逻辑层代码文件");
        }
    }
}
/**
 * 示意生成数据层的模块
 */
public class DAO {
    public void generate() {
        // 1:从配置管理里面获取相应的配置信息
        ConfigModel cm = ConfigManager.getInstance().getConfigData();
        if (cm.isNeedGenDAO()) {
            // 2:按照要求去生成相应的代码,并保存成文件
            System.out.println("正在生成数据层代码文件");
        }
    }
}

(4)此时的客户端实现,就应该自行去调用这多个模块了,示例代码如下:

public class Client {
    public static void main(String[] args) {
        // 现在没有配置文件,就直接使用默认的配置
        // 通常情况下,三层都应该生成,也就是说客户端必须
        // 对这些模块都有了解,才能够正确使用它们
        new Presentation().generate();
        new Business().generate();
        new DAO().generate();
    }
}

运行结果如下:

正在生成表现层代码文件
正在生成逻辑层代码文件
正在生成数据层代码文件
有何问题

仔细查看上面的实现,会发现其中有一个问题:那就是客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块交互

这对于客户端而言,是个麻烦,使得客户端不能简单的使用生成代码的功能。而且,如果其中的某个模块发生了变化,还可能会引起客户端也需要跟着变化。

那么如何实现,才能让子系统外部的客户端在使用子系统的时候,既能简单的使用这些子系统内部的模块功能,而又不用客户端去与子系统内的多个模块交互呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值