一天一模式之3外观模式

			原博文:https://blog.csdn.net/KongZhongNiao/article/details/79568736

定义

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层
接口,这个接口使得这一子系统更加容易使用。

结构和说明

一天一模式之3外观模式
- Facade:定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,
从而把客户的请求代理给适当的子系统对象。
- 模块:接受Facade对象的委派,真正实现功能,各个模块之间可能有交互。

- 注意,Facade对象知道各个模块,但是各个模块不应该知道Facade对象。

示例代码

A模块的接口

package cn.javass.dp.facade.example2;
/**
 * A模块的接口
 */
public interface AModuleApi {
    /**
     * 示意方法,A模块对外的一个功能方法
     */
    public void testA();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

A模块的接口的实现

package cn.javass.dp.facade.example2;

public class AModuleImpl implements AModuleApi{
    public void testA() {
        System.out.println("现在在A模块里面操作testA方法");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

B模块的接口

package cn.javass.dp.facade.example2;

public interface BModuleApi {
    public void testB();
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

B模块的接口的实现

package cn.javass.dp.facade.example2;

public class BModuleImpl implements BModuleApi{


    public void testB() {
        System.out.println("现在在B模块里面操作testB方法");
    }

}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

C模块的接口

package cn.javass.dp.facade.example2;

public interface CModuleApi {
    public void testC();
}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

C模块的接口的实现

package cn.javass.dp.facade.example2;

public class CModuleImpl implements CModuleApi{


    public void testC() {
        System.out.println("现在在C模块里面操作testC方法");
    }

}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

外观对象

package cn.javass.dp.facade.example2;
/**
 * 外观对象
 */
public class Facade {
    /**
     * 示意方法,满足客户需要的功能
     */
    public void test(){
        //在内部实现的时候,可能会调用到内部的多个模块
        AModuleApi a = new AModuleImpl();
        a.testA();
        BModuleApi b = new BModuleImpl();
        b.testB();
        CModuleApi c = new CModuleImpl();
        c.testC();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

客户端

package cn.javass.dp.facade.example2;

public class Client {
    public static void main(String[] args) {
//      //不用Facade,需要自己跟多个模块交互
//      AModuleApi a = new AModuleImpl();
//      a.testA();
//      BModuleApi b = new BModuleImpl();
//      b.testB();
//      CModuleApi c = new CModuleImpl();
//      c.testC();
//      
//      System.out.println("使用Facade----------------------〉");
        //使用了Facade
        new Facade().test();        
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

生活中的示例——组装电脑

1:完全自己组装

一天一模式之3外观模式

2:找专业装机公司组装

一天一模式之3外观模式

代码生成的应用

一天一模式之3外观模式

问题

如果现在客户端需要使用这个代码生成工具来生成需要的基础代码,该如
何实现呢?

不用模式的解决方案

直接参看代码示例
示意生成数据层的模块
package cn.javass.dp.facade.example1;
/**
 * 示意生成数据层的模块
 */
public class DAO {
    public void generate(){
        //1:从配置管理里面获取相应的配置信息
        ConfigModel cm = ConfigManager.getInstance().getConfigData();
        if(cm.isNeedGenDAO()){
            //2:按照要求去生成相应的代码,并保存成文件
            System.out.println("正在生成数据层代码文件");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
示意生成表现层的模块
package cn.javass.dp.facade.example1;
/**
 * 示意生成表现层的模块
 */
public class Presentation {
    public void generate(){
        //1:从配置管理里面获取相应的配置信息
        ConfigModel cm = ConfigManager.getInstance().getConfigData();
        if(cm.isNeedGenPresentation()){
            //2:按照要求去生成相应的代码,并保存成文件
            System.out.println("正在生成表现层代码文件");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
示意生成逻辑层的模块
package cn.javass.dp.facade.example1;
/**
 * 示意生成逻辑层的模块
 */
public class Business {
    public void generate(){
        //1:从配置管理里面获取相应的配置信息
        ConfigModel cm = ConfigManager.getInstance().getConfigData();
        if(cm.isNeedGenBusiness()){
            //2:按照要求去生成相应的代码,并保存成文件
            System.out.println("正在生成逻辑层代码文件");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
示意配置描述的数据Model,真实的配置数据会很多
package cn.javass.dp.facade.example1;
/**
 * 示意配置描述的数据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;
    }
}

  • 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
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
示意配置管理,就是负责读取配置文件,
package cn.javass.dp.facade.example1;
/**
 * 示意配置管理,就是负责读取配置文件,
 * 并把配置文件的内容设置到配置Model中去,是个单例
 */
public class ConfigManager {
    private static ConfigManager manager = ;
    private static ConfigModel cm = ;
    private ConfigManager(){

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

  • 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
客户端
package cn.javass.dp.facade.example1;

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

        new Presentation().generate();

        new Business().generate();

        new DAO().generate();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

存在的问题

客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块交互。

使用模式的解决方案

一天一模式之3外观模式

红线内表示被封装隔离开
红线外facade表示与客户端交互

使用外观模式的示例代码

其他类没有变,增添了facade外观类

代码生成子系统的外观对象
package cn.javass.dp.facade.example3;
/**
 * 代码生成子系统的外观对象
 */
public class Facade {

    /**
     * 客户端需要的,一个简单的调用代码生成的功能
     */
    public  void generate(){
        new Presentation().generate();
        new Business().generate();
        new DAO(2).generate();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
客户端
package cn.javass.dp.facade.example3;

public class Client {
    public static void main(String[] args) {
        new Facade().generate();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

理解外观模式

认识外观模式

  • 1:外观模式的目的
    外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与
    子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。

  • 2:使用外观跟不使用相比有何变化
    Facade方便了客户端的调用、封装了系统内部的细节功能、实现功能的共
    享和复用

  • 3:有外观,但是可以不使用

  • 4:外观提供了缺省的功能实现

  • 5: 外观模式的调用顺序示意图
    一天一模式之3外观模式

外观模式的实现

- 1:把外观类当成一个辅助工具类实现

代码生成子系统的外观对象

package cn.javass.dp.facade.example3;
/**
 * 代码生成子系统的外观对象
 */
public class Facade {
    private Facade(){

    }
    /**
     * 客户端需要的,一个简单的调用代码生成的功能
     */
    public static void generate(){
        new Presentation().generate();
        new Business().generate();
        new DAO(2).generate();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

客户端

package cn.javass.dp.facade.example3;

public class Client {
    public static void main(String[] args) {
        Facade.generate();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

- 2:Facade可以实现成为interface

一天一模式之3外观模式

Facadeapi接口

package cn.javass.dp.facade.example5;

public interface FacadeApi {
    public void generate();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Facade实现

package cn.javass.dp.facade.example5;
/**
 * 代码生成子系统的外观对象
 */
public class Facade implements FacadeApi{
    /**
     * 客户端需要的,一个简单的调用代码生成的功能
     */
    public  void generate(){
        new Presentation().generate();
        new Business().generate();
        new DAO(2).generate();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Facade工厂

package cn.javass.dp.facade.example5;

public class FacadeFactory {
    private FacadeFactory(){

    }

    public static FacadeApi createFacadeApi(){
        return new Facade();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

客户端

package cn.javass.dp.facade.example5;

public class Client {
    public static void main(String[] args) {
        FacadeApi api = FacadeFactory.createFacadeApi();

        api.generate();
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

- 3:Facade实现成为interface的附带好处

能够有选择性的暴露接口方法,尽量减少模块对子系统外提供的接口方法。

AModuleApi

package cn.javass.dp.facade.example4;

public interface AModuleApi {
    //提供给子系统外部使用
    public void a1();

    //提供给同一个子系统中,不同的模块使用,比如给B模块,给C模块
    public void a2();
    public void a3();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

BModuleApi

package cn.javass.dp.facade.example4;

public interface BModuleApi {
    //对子系统外部
    public void b1();

    //子系统内部使用
    public void b2();
    //子系统内部使用
    public void b3();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

CModuleApi

package cn.javass.dp.facade.example4;

public interface CModuleApi {
    //对子系统外部
    public void c1();

    //子系统内部使用
    public void c2();
    //子系统内部使用
    public void c3();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

FacadeApi

package cn.javass.dp.facade.example4;

public interface FacadeApi {
    public void a1();
    public void b1();
    public void c1();

    public void test();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

- 4:Facade的方法实现

Facade的方法实现中,一般是负责把客户端的请求转发给子系统内部的各
个模块进行处理,Facade的方法本身并不进行功能的处理,Facade的方法的实现
只是实现一个功能的组合调用。

思考外观模式

外观模式的优缺点

  • 1:松散耦合
  • 2:简单易用
  • 3:更好的划分访问层次

- 4:过多的或者是不太合理的Facade也容易让人迷惑

外观模式的本质

外观模式的本质是:封装交互,简化调用

对设计原则的体现

体现了“最少知识原则”

何时选用外观模式

  • 1:如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模
    式,使用外观对象来实现大部分客户需要的功能,从而简化客户的使用
  • 2:如果想要让客户程序和抽象类的实现部分松散耦合,可以考虑使用外观模式,使
    用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性和可
    移植性
  • 3:如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入
    口,这样可以简化层间调用,也可以松散层次之间的依赖关系
上一篇:Design Pattern - Facade(Java)


下一篇:【35.29%】【codeforces 557C】Arthur and Table