外观模式
今天学习一个新的设计模式---外观模式,对于这个模式可能看名称不是很有印象,但实际上还是给客户端解耦,让其调用更加简单、方便,具体请看下面进行一一分解:
定义:
为了系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用(对于这个概念可能不是太好理解,先抛出理解,之后看下代码之后就能理解了,请继续往下看!)
结构及说明:
  
Facade:定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,从而把客户的请求代理给适当的子系统对象
模块:接受Facade对象的委派,真正实现功能,各个模块之间可能有交互。
(注意:Facade对象知道各个模块,但是各个模块不应该知道Facade对象)
对于上面的概念可能不是那么直观清晰,下面举一个生活中的一个例子来进一步阐述Facade模式的意义。
组装电脑,我想大家都曾经有过体会,会有如下两种情况:
1、自已完全组装:

说明:各个配件都得我们亲自去相应的零件公司去挑选,然后再自己组装好,对于自己来说,是一件非常非常辛苦的工作!
2、找专业装机公司组装:

说明:由于有了装机公司的加入,客户自己就完全不用管配件从哪个厂家买过来,及如何装配了,对于自己来说,既轻松,又省事!而Facade模式就是扮演"装机公司"的角色,通过这个形象的生活例子,应该就比较容易理解这个模式的定义了吧!
下面正式进入代码阶段来加深对模式的理解,以一个“代码生成的应用”来例子,来体现一下外观模式:

这个代码生成工具是由多个生成模块组成的,所以现在客户端需要使用这个代码生成工具来生成需要的基础代码,该如何实现呢?以下用两种方案来进行对比,进一步突出外观模式的作用。
不用模式的解决方案:
工程结构如下:
 
下面具体来展现各个类的代码,就知道其调用关系了:
ConfigModel【这里面主要是存放配置开关,是否要生成某个模块的代码】:
/** * 示意配置描述的数据Model,真实的配置数据会很多 */ public class ConfigModel { /** * 是否需要生成表现层,默认是true */ private boolean needGenPresentation = true; /** * 是否需要生成逻辑层,默认是true */ private boolean needGenBusiness = true; /** * 是否需要生成DAO,默认是true */ private boolean needGenDAO = true; public boolean isNeedGenPresentation() { return needGenPresentation; } public void setNeedGenPresentation(boolean needGenPresentation) { this.needGenPresentation = needGenPresentation; } public boolean isNeedGenBusiness() { return needGenBusiness; } public void setNeedGenBusiness(boolean needGenBusiness) { this.needGenBusiness = needGenBusiness; } public boolean isNeedGenDAO() { return needGenDAO; } public void setNeedGenDAO(boolean needGenDAO) { this.needGenDAO = needGenDAO; } }
ConfigManager.java【配置管理器,主要是供模块来调用的】:
/** * 示意配置管理,就是负责读取配置文件, 并把配置文件的内容设置到配置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; } }
以上是配置相关的类,以下则是各个生成模块的代码了,都调用了以上配置管理器:
Presentation.java:
/** * 示意生成表现层的模块 */ public class Presentation { public void generate() { // 1:从配置管理里面获取相应的配置信息 ConfigModel cm = ConfigManager.getInstance().getConfigData(); if (cm.isNeedGenPresentation()) { // 2:按照要求去生成相应的代码,并保存成文件 System.out.println("正在生成表现层代码文件"); } } }
Business.java:
/** * 示意生成逻辑层的模块 */ public class Business { public void generate() { // 1:从配置管理里面获取相应的配置信息 ConfigModel cm = ConfigManager.getInstance().getConfigData(); if (cm.isNeedGenBusiness()) { // 2:按照要求去生成相应的代码,并保存成文件 System.out.println("正在生成逻辑层代码文件"); } } }
DAO.java:
/** * 示意生成数据层的模块 */ public class DAO { public void generate() { // 1:从配置管理里面获取相应的配置信息 ConfigModel cm = ConfigManager.getInstance().getConfigData(); if (cm.isNeedGenDAO()) { // 2:按照要求去生成相应的代码,并保存成文件 System.out.println("正在生成数据层代码文件"); } } }
这时客户端要拿生成工具开始生成代码了,代码如下:
Client.java:
public class Client { public static void main(String[] args) { new Presentation().generate(); new Business().generate(); new DAO().generate(); } }
输出结果如下:

解析:从客户端的生成代码来看,需对各个生成模块有所了解才能够生成代码,也就是与客户端耦合得太紧密了,这不是一个很好的程序!!于是请继续看下面的解决之道。
使用模式的解决方案:
工程结构如下:

其它的模块生成类都不变,只是加入了Facade类来改变了与客户端的交互方式,至于具体有啥变化,请看下面的代码展示,其实很简单:
Facade.java:
/** * 代码生成子系统的外观对象 */ public class Facade { private Facade() { } /** * 将一个简单的调用代码生成的功能暴露给客户端,让它不用关心具体的细节,从而达到了解耦 */ public static void generate() { new Presentation().generate(); new Business().generate(); new DAO().generate(); } }
这回再看下客户端的调用:
Client.java:
public class Client { public static void main(String[] args) { // 直接调用一个方法,就可以生成相印的代码了,对于客户端,生成细节是透明的 Facade.generate(); } }
解析:从客户端的生成代码来看,使用了Facade模式之后,封装了系统内部的细节功能,方便了客户端的调用,并且让客户端与系统之间解耦。
通过上面的对比,我想应该对于外观模式有了很直观的认识,下面对其进行一下概念上的进一步理解(虽说偏理论,但是有助于更深刻的理解它):
1、外观模式的目的:
外观模式的目的不是给予系统添加新的功能接口(好像是增加了一个Facade类,里面暴露了一个接口),而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。
2、外观模式的调用顺序示意图:

其中图中关键字“可能”说明我们可以灵活的组织我们要调用的模块,最后暴露给客户端,下面还有两个特点需要再说明下,重在体会:
a,有外观模式,但是可以不使用
对于外观模式中的方法,客户端可以针对性的使用,不一定非要使用(有点费话呵,简单体会下既可)
b,外观提供了一种缺省的功能实现
这是说的啥意思呢?拿我们举的代码生成的例子来说,Facade.java中的generate方法是一种给客户端提供的一种默认的代码生成规则,对于客户端而言,可以完全不用这种规则,而自己去拿到不同的生成模块去生成。
下面来对Facade模式做一下改变,让其与简单工厂进行结合,因为Facade可以实现成为interface(不要以为它只能做为具体的类哦!),具体怎么一个表现法,直接先上代码,然后再事后做说明:
说明:简单工厂在上一篇博文中已经做了详细的介绍,地址:http://www.cnblogs.com/webor2006/p/3360252.html
使用接口(facade模式+简单工厂模式)的解决方案:
工程结构如下:

下面列出变化的类:
FacadeApi.java:
/*** * 定义一个代码生成接口 */ public interface FacadeApi { public void generate(); }
Facade.java:
/** * 代码生成子系统的外观对象 */ public class Facade implements FacadeApi { /** * 客户端需要的,一个简单的调用代码生成的功能 */ public void generate() { new Presentation().generate(); new Business().generate(); new DAO().generate(); } }
FacadeFactory.java:
/** * Facade简单工厂,用来创建具体的Facade对象 */ public class FacadeFactory { private FacadeFactory() { } public static FacadeApi createFacadeApi() { return new Facade(); } }
Client.java:
public class Client { public static void main(String[] args) { // 客户端通过简单工厂来获得Facade接口,最终生成代码 FacadeApi api = FacadeFactory.createFacadeApi(); api.generate(); } }
对于上面这种面向接口的方式,用一个简单的图来描述一下:

注意:
Facade的方法实现中,一般是负责把客户端的请求转发给子系统内部的各个模块进行处理,Facade的方法本身并不进行功能的具体实现(也就是Facade类里面并不实现客户端的具体业务逻辑,而只是把业务委派给专门模块进行处理,只是个转发的过程),Facade的方法的实现只是实现一个功能的组合调用。
通过以上详细的描述,下面最后再总结一下Facade模式,结合上面的例子可以细细体会下:
Facade模式的优缺点
1、松散耦合【这一点应该可以很清晰的理解到】
2、简单易用
3、更好的划分访问层次
4、过多的或者是不太合理的Facade也容易让人迷惑【需要合理的去使用这种模式】
它的本质:
封装交互、简化调用
使用场景
1、如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模式,使用外观对象来实现大部分客户需要的功能,从而简化客户的使用。
2、如果想要让客户端和抽象类的实现部分进行松散耦合,可以考虑使用外观模式,使用外观对象来将这个子系统与它的客户端分离开来,从而提高子系统的独立性和可移植性。
3、如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入口,这样可以简化层与层之间的调用,也可以松散层与层之间的依赖关系。
好了,该模式就介绍到这,下个模式再见!!
 
                    
                 
                
            
         
 浙公网安备 33010602011771号
浙公网安备 33010602011771号