外观模式

  • 定义:又叫门面模式,提供了一个统一的接口或外观类,用来访问子系统中的一群接口
  • 特点:外观模式定义了一个高层接口,让子系统更容易使用
  • 类型:结构型
  • 适用场景:
    •   子系统越来越复杂,增加外观模式提供简单调用接口
    •   构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
  • 优点:
    •   简化了调用过程,无需深入了解子系统,防止带来风险
    •   减少系统依赖、松散耦合,外观模式松散了客户端与子系统的耦合关系,客户端不与子系统直接交流,客户端和外观对象直接交流,让子系统内部的模块更容易扩展和维护
    •   更好的划分访问层次
    •   符合迪米特原则,即最少知道原则
  • 缺点:
    •   增加子系统、扩展子系统行为容易引入风险
    •   增加扩展子系统不符合开闭原则
  • 相关设计模式:
    •   外观模式和中介者模式:外观模式关注的是外界和子系统之间的交互,中介者模式关注的是子系统内部之间的交互
    •   外观模式和单例模式:通常会把外观模式中的外观对象做成单例模式
    •   外观模式和抽象工厂模式:外观类可以通过抽象工厂获取子系统的实例,这样子系统在内部可以对外观类进行屏蔽

coding

moduleA

/**
 * <p>模块A下的子系统A</p>
 */
public class SubSystemA {

    public void initSystem() {
        System.out.println("人工智能系统正在启动,请您稍等.... =====>" + this);
        for (int i = 0; i < 3; i++) {
            try {
                System.out.println("等待" + (i + 1) + "s");
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println(ex.getClass() + "," + ex.getMessage());
            }
        }
    }

    @Override
    public String toString() {
        return "人工智能系统,模块A,子系统A,主要负责启动系统";
    }
}
/**
 * <p>模块A下的子系统B</p>
 *
 * @author Appleyk
 * @version v0.1.1
 * @blob https://blog.csdn.net/appleyk
 * @date Created on 上午 9:00 2018-11-12
 */
public class SubSystemB {

    public void loadDatas() {
        System.out.println("人工智能系统已经启动,正在加载数据 =====>" + this);
        System.out.println(".........");
        System.out.println("数据已完成加载");
    }

    @Override
    public String toString() {
        return "人工智能系统,模块A,子系统B,主要负责加载数据";
    }
}
/**
 * <p>外观模式A == 聚合子模块A中的子系统A和B,降低客户端操作子模块A的复杂度</p>
 */
public class FacadeA {

    private SubSystemA subSystemA;
    private SubSystemB subSystemB;

    public FacadeA() {
        subSystemA = new SubSystemA();
        subSystemB = new SubSystemB();
    }

    /**
     * 将模块A的功能封装一下,松散客户端与A模块中的各个子系统间的耦合关系
     * 让模块中的各个子系统更容易扩展和维护
     */
    public void initialize() {
        subSystemA.initSystem();
        subSystemB.loadDatas();
    }
}

moduleB

/**
 * <p>模块B下的子系统C</p>
 */
public class SubSystemC {

    public void sayHello() {
        System.out.println("欢迎进入Appleyk's 基于电影知识图谱的人工智能系统 =====>" + this);
    }

    @Override
    public String toString() {
        return "人工智能系统,模块B,子系统C,欢迎界面";
    }
}
/**
 * <p>模块B下的子系统D</p>
 *
 * @author Appleyk
 * @version v0.1.1
 * @blob https://blog.csdn.net/appleyk
 * @date Created on 上午 9:00 2018-11-12
 */
public class SubSystemD {

    public void working() {
        System.out.println("亲爱的用户,以下是你18年截止到今天看的电影的大数据统计结果:=====>" + this);
        System.out.println("《《《《 ========== 》》》》》\n爱情动作片:10次\n" +
                "恐怖片:1次\n" +
                "喜剧片:6次\n" +
                "总花费:680.5元\n" +
                "《《《《 ========== 》》》》》");
    }

    @Override
    public String toString() {
        return "人工智能系统,模块B,子系统D,开始工作";
    }
}

/**
 * <p>外观模式B == 聚合子模块B中的子系统C和D,降低客户端操作子模块B的复杂度</p>
 */
public class FacadeB {

    private SubSystemC subSystemC;
    private SubSystemD subSystemD;

    public FacadeB() {
        subSystemC = new SubSystemC();
        subSystemD = new SubSystemD();
    }

    /**
     * 将模块A的功能封装一下,松散客户端与A模块中的各个子系统间的耦合关系
     * 让模块中的各个子系统更容易扩展和维护
     */
    public void work() {
        subSystemC.sayHello();
        subSystemD.working();
    }
}

外观模式AB

/**
 * <p>外观模式AB == 聚合子模块A和子模块B的功能,降低客户端使用模块A和模块B的复杂度</p>
 */
public class FacadeAB {

    private FacadeA facadeA;
    private FacadeB facadeB;

    public FacadeAB() {
        facadeA = new FacadeA();
        facadeB = new FacadeB();
    }

    public void startSystem() {
        facadeA.initialize();
        facadeB.work();
    }
}

测试

/**
 * <p>外观模式测试</p>
 */
public class FacadeTest {

    public static void main(String[] args) {

        /**
         * 不使用外观模式,直接使用智能人工系统的模块A和模块B的功能
         * 缺点:用户必须清楚模块中的各个子系统的工作流程,否则会导致系统的不正常工作
         */
        useModuleA();
        useModuleB();

        /**
         * 使用外观模式A和外观模式B
         * 缺点:依然不够简洁
         */
        useModuleAByFacadeA();
        useModuleAByFacadeB();

        /**
         * 使用外观模式AB
         * 优点:用户使用系统,相当的便捷,没有多余的废话
         */
        useSystemByFacadeAB();

        /**
         * 通过以上的对比,应用层(FacadeTest )是不需要关心子系统,只要保证和外观类(FacadeAB )通信即可,具体里面初始化,加载数据等,应用层不需要关心
         */
    }

    /**
     * 不使用外观模式,直接使用模块A的功能
     */
    private static void useModuleA() {
        partition("直接使用模块A中的功能");
        SubSystemA subSystemA = new SubSystemA();
        SubSystemB subSystemB = new SubSystemB();
        subSystemA.initSystem();
        subSystemB.loadDatas();
    }

    /**
     * 不使用外观模式,直接使用模块B的功能
     */
    private static void useModuleB() {
        partition("直接使用模块B中的功能");
        SubSystemC subSystemC = new SubSystemC();
        SubSystemD subSystemD = new SubSystemD();
        subSystemC.sayHello();
        subSystemD.working();
    }

    /**
     * 使用外观模式A,对模块A中的功能进行"屏蔽"
     */
    private static void useModuleAByFacadeA() {
        partition("使用外观模式A");
        FacadeA facadeA = new FacadeA();
        facadeA.initialize();
    }

    /**
     * 使用外观模式B,对模块B中的功能进行"屏蔽"
     */
    private static void useModuleAByFacadeB() {
        partition("使用外观模式A");
        FacadeB facadeB = new FacadeB();
        facadeB.work();
    }

    /**
     * 使用外观模式AB,对外观模式A和B再进行一次"屏蔽"
     */
    private static void useSystemByFacadeAB() {
        partition("使用外观模式AB");
        FacadeAB facadeAB = new FacadeAB();
        facadeAB.startSystem();
    }

    private static void partition(String note) {
        System.out.println("============== 分割线【" + note + "】 ==============");
    }
}

通过以上的对比,应用层(FacadeTest )是不需要关心子系统,只要保证和外观类(FacadeAB )通信即可,具体里面初始化,加载数据等,应用层不需要关心

 

UML

从UML中可以看出,它非常完美的支持了迪米特法则,我们这里使用的是实体外观类,但是如果要增加另一个子系统,就需要修改实体外观类,从这个角度分析并不符合开闭原则,我们可以再扩展一下,如果这个外观类使用抽象外观类的话,也就是实体外观类可以实现实体外观接口去操作。

posted @ 2024-01-15 10:43  wangzhilei  阅读(5)  评论(0编辑  收藏  举报