二十二、外观设计模式

1. 外观设计模式介绍

显示生活中有一个种电视万能遥控器,只要和电视配对好了以后,就可以正常使用,不同型号的电视,只要一旦适配,所有的操作模式一模一样。

这就是一种外观适配模式。表面上都是同一个遥控器,实际上不同型号的电视,不同的操作,发出的型号可能各不相同。但是对于用户来说,没有任何差别。

定义

要求一个子系统的外部和其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易使用。

2. 外观设计模式使用场景

  • 为一个复杂的子系统提供简单、固定的接口。比如我们常用的加载图片的框架,随着项目的推进,框架没有人维护,或者出现了效率更高的框架出现,此时如果项目进行更换框架,可能需要修改很多处。如果,项目特别大,更是灾难。但是如果我们之前采用了外观设计模式,设计了统一的封装接口,我们只需要接口里面详细的逻辑即可,而不必修改很多出。从外观就好像没有修改一样。
  • 当构建一个层次结构的子系统时,使用外观设计模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,那么子系统之间可以通过外观设计模式定义的接口进行通讯,简化子系统之间的依赖关系。

3. 外观设计模式UML类图

4. 外观设计模式简单实例

现在以一个情形:一个子系统拥有三个模块,每个模块都有三个方法,其中一个为客户端调用方法,其它两个为各个子模块间互相调用方法。此时客户端需要组合三个模块中的方法才能完成功能。

  • (1)、A、B、C三个模块方法:

A 模块方法接口:

public interface AModuleApi {
    //此方法用于外部调用
    public void a1();
    //以下两个方法主要用于子系统内部间系统调用
    public void a2();
    public void a3();
}

A模块具体实现:

public class AModuleImpl implements AModuleApi {
    @Override
    public void a1() {
        System.out.println("调用了A模块");
    }

    @Override
    public void a2() {
        //主要用于子模块间相互调用
    }

    @Override
    public void a3() {
        // 主要用于子模块间相互调用
    }
}

以上是A模块中的方法,B、C两个模块和A一样。

  • (2)、不采用外观设计模式,客户端如下:
public class Client {
    public static void main(String[] args) {

        AModuleApi aModule = new AModuleImpl();
        aModule.a1();

        BModuleApi bModule = new BModuleImpl();
        bModule.b1();

        CModuleImpl cModule = new CModuleImpl();
        cModule.c1();
    }
}

上面的代码我们会经常写,自习详细,会存在如下问题:

  • 代码耦合度太高,客户端与子系统中各模块都有关联。一旦子系统有什么更改,会涉及到客户端的修改。
  • 客户端想要知道每个模块中各个方法的含义才能进行调用。

现在采用了外观设计模式,添加一个外观类,由外观重组需要调用的方法

  • (3)、添加一个外观类接口:
public interface FacadeApi {
    public void a1();

    public void b1();

    public void c1();

    public void test();
}
  • (4)、外观类的具体实现:
public class FacadeImpl implements FacadeApi {
    @Override
    public void a1() {
        new AModuleImpl().a1();
    }

    @Override
    public void b1() {
        new BModuleImpl().b1();
    }

    @Override
    public void c1() {
        new CModuleImpl().c1();
    }

    @Override
    public void test() {
        a1();
        b1();
        c1();
    }
}

在test方法中,调用了三个模块中的方法。外观类是在服务端的,不是这客户端。客户端不需要知道具体的内部实现是什么样的。

  • (5)、客户端:
public class Client {
    public static void main(String[] args) {
        /**
         * 直接调用外观类里面的方法就可以了,内部细节不用知道,
         * 计算改变了,也只需要调用这两行代码即可。
         */
        FacadeApi facade = new FacadeImpl();
        facade.test();
    }
}

客户端只需要调用外观模式中的代码即可,不需要关注具体实现细节。

5. 外观设计模式在Android源码

我们经常在Activity中调用其他的方法,比如,Activity的跳转,发送广播,启动服务等。这些方法都是封装在Context类中,Context是一个抽象类,具体实现是ContextImpl。

ContextImpl内部封装了很多不同子系统的操作,上面说的这些操作就是在这里面,然后它的具体实现并不在ContextImpl里面,而是在各个不同的子系统进行处理。

这张图就是ContextImpl和各个子类系统之间的调用关系:

6. 外观设计模式在Android开发中

在开发中,我们经常会用到图片加载,网络加载框架。有时候,随着时间的推移,我们所使用框架的作者有可能不再维护,或者出现了效率更高的新的框架。

这时如果我们想要修改为新的框架,可能比较困难,可能太多的地方用到了。

但是如果我们采用了外观设计模式,用了一个外观类来包装,调用的时候直接调用外观方法,而不是直接调用。以后在需要修改的时候,只需要修改包装类的方法即可。

实例:

public class ImageLoaer {

    public Image loadImage(String url, int width, int height) {
        //调用具体的图片框架方法加载
    }
}

7. 总结

  • 优点:

    • 对客户端程序隐藏子系统细节,减少了客户端对子系统的耦合。
    • 外观类对子系统的封装,使得系统更易于使用。
时间: 2024-11-06 07:15:18

二十二、外观设计模式的相关文章

(转)Inno Setup入门(二十二)——Inno Setup类参考(8)

本文转载自:http://blog.csdn.net/yushanddddfenghailin/article/details/17268473 列表框 列表框(ListBox)是Windows应用程序中重要的输入手段,其中包括多个选项用户可以从其中选择一个或者多个,程序根据用户的选择做出相应的处理,列表框在外观上和存储框类似,但是行为却有很大的不同,列表框中项一般是预先给定的,而存储框则可以让用户进行输入,并且列表框中的项被选择之后也会触发事件.Pascal脚本中列表框的类由TlistBox实

【管理心得之二十二】小人物 仰视 大授权

场景再现====================Boss:小王,来我办公室一下.小王: 嗯Boss:近期总公司有会,需要到外地出差几日.我不在的这段期间里,公司大小事务你帮忙处理一下.          如果有什么难决定的事,第一时间电话.邮件联系我商定即可.小王:  明白.放心吧领导,绝不会让你失望的Boss:嗯,那就好,没事了. {小王走出办公室} 心中暗喜,"难道这就是传说中的授权,Boss不在的时候,我岂不是最高权力的行使者." ==================== 从场景

QT开发(二十二)——QMainWindow主窗口

QT开发(二十二)--QMainWindow主窗口 一.主窗口简介 应用程序中的主窗口是与用户进行长时间交互的顶层窗口,提供了应用程序的大部分功能,通常是应用程序启动后的第一个窗口,应用程序一般由一个主窗口和多个对话框组成. QT中直接支持主窗口,QMainWindow是QT中主窗口的基类,是继承于QWidget类的容器型组件. QMainWindow内部封装了菜单栏.工具栏.中心组件.停靠组件.状态栏等. QMainWindow内置了布局管理器,基本的组件布局如下: 二.菜单栏 QT中提供了预

《Programming in Lua 3》读书笔记(二十二)

日期:2014.8.6 PartⅣ The C API 26 Extending Your Application 使用Lua很重要的一点是用来做配置语言.配合主语言做一些功能的配置. 26.1 The Basics 有的时候程序需要配置一些功能信息,很多时候可能有许多别的方法比用lua做配置要更简单:如使用环境变量或者读取文件,读取文件涉及到文件的解析.如果使用Lua进行配置的话,相当于用lua文件替代了要读取的如csv.txt文件等. 使用Lua进行配置的时候,就需要使用Lua API去控制

企业搜索引擎开发之连接器connector(二十二)

下面来分析线程执行类,线程池ThreadPool类 对该类的理解需要对java的线程池比较熟悉 该类引用了一个内部类 /** * The lazily constructed LazyThreadPool instance. */ private LazyThreadPool lazyThreadPool; 该成员实现了单例模式,即该对象只有一个实例,属于懒汉式单例模式,当实例化该成员时,启用了线程同步机制 /** * Shut down the {@link ThreadPool}. Afte

JAVA之旅(二十二)——Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习

JAVA之旅(二十二)--Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习 继续坚持下去吧,各位骚年们! 事实上,我们的数据结构,只剩下这个Map的知识点了,平时开发中,也是能看到他的,所以还是非常值得去学习的一个知识点的,我们直接开车了 一.Map概述 泛型< k,v> 键值对,映射关系 基本特点 该集合存储键值对,是一对一对往里存,而且要保证键的唯一性 1.添加 put(key ,values) putAll() 2.删除 clear() remove(ob

每日算法之二十二:Swap Nodes in Pairs

Given a linked list, swap every two adjacent nodes and return its head. For example, Given 1->2->3->4, you should return the list as 2->1->4->3. Your algorithm should use only constant space. You may not modify the values in the list, on

攻城狮在路上(叁)Linux(二十二)--- linux磁盘挂载与卸载 mount umount

挂载就是将文件系统与目录结合的操作.挂载点就是目录,该目录就是进入分区或文件系统的入口. 一.挂载前的注意事项: 1.单一文件系统不应该被重复挂载在不同的挂载点中. 2.单一目录不应该重复挂载多个文件系统. 3.即将作为挂载点的目录理论上应该都是空目录才对. 注意:对应2和3,如果要挂载的目录不为空,那么挂载了新的文件系统之后,原来的目录会暂时消失(并非覆盖掉),系统会显示最新挂载的文件系统.新分区被卸载之后,原来的文件系统会重新显示出来. 二.命令介绍 mount [-t 文件系统] [-L

爪哇国新游记之二十二----算术表达式计算求值

代码: import java.util.ArrayList; import java.util.List; // 辅助类 class Item{ String value; boolean isNumber; public Item(String value,boolean isNumber){ this.value=value; this.isNumber=isNumber; } public Item(char c,boolean isNumber){ this.value=String.

Android学习路线(二十二)运用Fragment构建动态UI——构建一个灵活的UI

先占个位置,下次翻译 :p When designing your application to support a wide range of screen sizes, you can reuse your fragments in different layout configurations to optimize the user experience based on the available screen space. For example, on a handset devi