作者QQ:1095737364    QQ群:123300273     欢迎加入!

1.模式定义:

  外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以直接通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合。

2.模式特点:

  外观模式的一般描述是:外观模式定义了一个高层的功能,为子系统中的多个模块协同的完成某种功能需求提供简单的对外功能调用方式,使得这一子系统更加容易被外部使用。
  外观模式的目的不是给予子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。
  外观模式的本质是:封装交互,简化调用。
  外观模式,为子系统的一组接口提供一个统一的接口,该模式定义了一个高层接口,这一个高层接口使的子系统更加容易使用。并且外观模式可以解决层结构分离、降低系统耦合度和为新旧系统交互提供接口功能。
  使用了外观模式之后,客户端只依赖与外观类,从而将客户端与子系统的依赖解耦了,如果子系统发生改变,此时客户端的代码并不需要去改变。外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。然而这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。

3.使用场景:

(1)为复杂系统 提供简单的接口。
(2) 客户程序与抽象类的实现部分分离。
(3)构建层次系统时,用作入口。需要将设计进行分层时考虑Facade模式。
(4)在开发阶段,子系统往往因为重构变得越来越复杂,增加外观模式可以提供一个简单的接口,减少它们之间的依赖。
(5) 在维护一个遗留的大型系统时,可以这个系统已经非常难以维护和扩展,可以为新系统开发一个Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

4.模式实现:

  (1)外观模式的结构

    由于外观模式的结构图过于抽象,因此把它稍稍具体点。假设子系统内有三个模块,分别是ModuleA、ModuleB和ModuleC,它们分别有一个示例方法,那么此时示例的整体结构图如下:
 在这个对象图中,出现了两个角色:

    [1]外观(Facade)角色 :

    客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
public class Facade {
//示意方法,满足客户端需要的功能
public void test(){
ModuleA a = new ModuleA();
a.testA();
ModuleB b = new ModuleB();
b.testB();
ModuleC c = new ModuleC();
c.testC();
}
}

    [2]子系统(SubSystem)角色 :

    可以同时有一个或者多个子系统。每个子系统都不是一个单独的类,而是一个类的集合(如上面的子系统就是由ModuleA、ModuleB、ModuleC三个类组合而成)。每个子系统都可以被客户端直接调用,或者被外观角色调用。子系统并不知道外观的存在,对于子系统而言,外观仅仅是另外一个客户端而已。
public class ModuleA {
//示意方法
public void testA(){
System.out.println("调用ModuleA中的testA方法");
}
}
public class ModuleB {
//示意方法
public void testB(){
System.out.println("调用ModuleB中的testB方法");
}
}
public class ModuleC {
//示意方法
public void testC(){
System.out.println("调用ModuleC中的testC方法");
}
}

    [3]客户端角色类:

public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.test();
}
}

    Facade类其实相当于A、B、C模块的外观界面,有了这个Facade类,那么客户端就不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在,客户端只需要跟Facade类交互就好了,从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统。

  (2)外观模式的实现

    使用外观模式还有一个附带的好处,就是能够有选择性地暴露方法。一个模块中定义的方法可以分成两部分,一部分是给子系统外部使用的,一部分是子系统内部模块之间相互调用时使用的。有了Facade类,那么用于子系统内部模块之间相互调用的方法就不用暴露给子系统外部了。

   [1]子系统(SubSystem)角色 :

public class Module {
/**
* 提供给子系统外部使用的方法
*/
public void a1(){};
/**
* 子系统内部模块之间相互调用时使用的方法
*/
public void a2(){};
public void a3(){};
}
public class ModuleB {
/**
* 提供给子系统外部使用的方法
*/
public void b1(){};
/**
* 子系统内部模块之间相互调用时使用的方法
*/
public void b2(){};
public void b3(){};
}
public class ModuleC {
/**
* 提供给子系统外部使用的方法
*/
public void c1(){};
/**
* 子系统内部模块之间相互调用时使用的方法
*/
public void c2(){};
public void c3(){};
}

    [2]外观(Facade)角色 :

public class ModuleFacade {

    ModuleA a = new ModuleA();
ModuleB b = new ModuleB();
ModuleC c = new ModuleC();
/**
* 下面这些是A、B、C模块对子系统外部提供的方法
*/
public void a1(){
a.a1();
}
public void b1(){
b.b1();
}
public void c1(){
c.c1();
}
}
    这样定义一个ModuleFacade类可以有效地屏蔽内部的细节,免得客户端去调用Module类时,发现一些不需要它知道的方法。比如a2()和a3()方法就不需要让客户端知道,否则既暴露了内部的细节,又让客户端迷惑。对客户端来说,他可能还要去思考a2()、a3()方法用来干什么呢?其实a2()和a3()方法是内部模块之间交互的,原本就不是对子系统外部的,所以干脆就不要让客户端知道。
一个系统可以有几个外观类
    在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只有一个外观类,而仅仅是说对每一个子系统只有一个外观类。或者说,如果一个系统有好几个子系统的话,每一个子系统都有一个外观类,整个系统可以有数个外观类。
为子系统增加新行为
    初学者往往以为通过继承一个外观类便可在子系统中加入新的行为,这是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。比如医院中的接待员并不是医护人员,接待员并不能为病人提供医疗服务。
 

  (3)外观模式在Tomcat中的使用

    Tomcat中外观模式使用的很多,因为Tomcat中有很多不同组件,每个组件要相互通信,但是又不能将自己内部数据过多的暴露给其他组件。外观面模式隔离数据是个很好的方法。
    下面是Request上使用的外观模式:
 
    使用过Servlet的人都清楚,除了要在web.xml做相应的配置外,还需继承一个叫HttpServlet的抽象类,并且重写doGet与doPost方法(当然只重写service方法也是可以的)。
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
    可以看出doGet与doPost方法有两个参数,参数类型是接口HttpServletRequest与接口HttpServletResponse,那么从Tomcat中传递过来的真实类型到底是什么呢?通过debug会发现,在真正调用TestServlet类之前,会经过很多Tomcat中的方法。如下图所示
 
  注意红色方框圈中的类,StandardWrapperValue类中的invoke方法225行代码如下:
      filterChain.doFilter(request.getRequest(), response.getResponse());
    在StandardWrapperValue类中并没有直接将Request对象与Response对象传递给ApplicationFilterChain类的doFilter方法,传递的是RequestFacade与ResponseFacade对象,为什么这么说呢,看一下request.getRequest()与response.getResponse()方法就真相大白了。

     [1]Request类

public HttpServletRequest getRequest() {
if (facade == null) {
facade = new RequestFacade(this);
}
return facade;
}

     [2]Response类

 public HttpServletResponse getResponse() {
if (facade == null) {
facade = new ResponseFacade(this);
}
return (facade);
}
    可以看到它们返回都是各自的一个外观类,那么这样做有什么好处呢?
    Request对象中的很多方法都是内部组件之间相互交互时使用的,比如setComet、setRequestedSessionId等方法(这里就不一一列举了)。这些方法并不对外部公开,但是又必须设置为public,因为还需要跟内部组件之间交互使用。最好的解决方法就是通过使用一个Facade类,将与内部组件之间交互使用的方法屏蔽掉,只提供给外部程序感兴趣的方法。
    如果不使用Facade类,直接传递的是Request对象和Response对象,那么熟悉容器内部运作的程序员可以分别把ServletRequest和ServletResponse对象向下转换为Request和Response,并调用它们的公共方法。比如拥有Request对象,就可以调用setComet、setRequestedSessionId等方法,这会危害安全性。
 

5.优缺点:

  (1)外观模式的优点:

   [1]松散耦合

    外观模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。

   [2]简单易用

    外观模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观类交互就可以了。

   [3]更好的划分访问层次

    通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好地隐藏了内部的细节。

  (2)外观模式的缺点:

[1]如果增加新的子系统可能需要修改外观类或客户端的源代码,这样就违背了”开——闭原则“(不过这点也是不可避免)。

[2]过多的或者是不太合理的Facade也容易让人迷惑,到底是调用Facade好呢,还是直接调用模块好。

[3]不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。

[4]在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

设计模式(10)--Facade(外观模式)--结构型的更多相关文章

  1. 设计模式(十一):FACADE外观模式 -- 结构型模式

    1. 概述 外观模式,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性.例子1:一个电源总开关可以控制四盏灯.一个风扇 ...

  2. 设计模式学习之路——Facade 外观模式(结构型模式)

    动机: 组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战.如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系 ...

  3. Facade 外观(结构型)

    Facade 外观(结构型) 一:描述: Facade 外观模式是为子系统至客户端之间提供简单的一致的接口,来降低耦合度. 二:模式图 三:实现代码简单例子: 1.业务模块: 2.外观接口: 3.客户 ...

  4. 面向对象设计模式之Facade外观模式(结构型)

    动机:有些系统组件的客户和组件中各种复杂的子系统有了过多的的耦合,随着外部客户程序  和个子系统的演化,这种过多的耦合面临很多变化的挑战:如何简化外部客户程序和系统的交互接口?  如何将外部客户程序的 ...

  5. 设计模式(7)--Bridge(桥接模式)--结构型

    1.模式定义: 桥接模式是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥接模式的用意是“将抽象化(Abstraction)与实现化(Impleme ...

  6. 设计模式(12)--Proxy(代理模式)--结构型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. ...

  7. 设计模式(9)--Composite(组合模式)--结构型

    1.模式定义: 组合模式属于对象的结构模式,有时又叫做“部分——整体”模式.组合模式将对象组织到树结构中,可以用来描述整体与部分的关系.组合模式可以使客户端将单纯元素与复合元素同等看待. 2.模式特点 ...

  8. 设计模式10: Facade 外观模式(结构型模式)

    Facade 外观模式(结构型模式) 系统的复杂度 假设我们要开发一个坦克模式系统用于模拟坦克车在各种作战环境中的行为,其中坦克系统由引擎.控制器.车轮.车身等各个子系统构成. internal cl ...

  9. (转载)设计模式学习笔记(十一)——Facade外观模式

    (转载)http://www.cnblogs.com/kid-li/archive/2006/07/10/446904.html Facade外观模式,是一种结构型模式,它主要解决的问题是:组件的客户 ...

随机推荐

  1. Shell - 简明Shell入门12 - 定制输出(ColorOutput)

    示例脚本及注释 #!/bin/bash echo -e "\033[32m" # 设置输出属性,绿色字体 echo "This is a test!" echo ...

  2. javascript之快速排序

    快速排序思想其实还是挺简单的,分三步走: 1.在数组中找到基准点,其他数与之比较. 2.建立两个数组,小于基准点的数存储在左边数组,大于基准点的数存储在右边数组. 3.拼接数组,然后左边数组与右边数组 ...

  3. 人工智能_机器学习——pandas - 箱型图

    箱型图对数据的展示也是非常清晰的,这是箱型图的一些代码 #导报 机器学习三剑客 import numpy as np import pandas as pd from matplotlib impor ...

  4. SQLAlchemy 快速入门、基础知识

    SQLAlchemy 是Python 编程语言下的一款开源软件.提供了SQL工具包及对象关系映射(ORM)工具. ORM, 全称Object Relational Mapping, 中文叫做对象关系映 ...

  5. 【LeetCode】128. 最长连续序列

    题目 给定一个未排序的整数数组,找出最长连续序列的长度. 要求算法的时间复杂度为O(n). 示例: 输入:[100, 4, 200, 1, 3, 2] 输出:4 解释:最长连续序列是[1, 2, 3, ...

  6. 【xsy2818】 最近点 动态树分治+可持久化线段树

    题目大意:给你一颗n个节点的树,最初点集S为空. 有m次操作:往当前点集S中加入/删除一个点,询问点x至集合S中任意点的最小距离,回到第t次修改点集的操作后的状态. 数据范围:$n,m≤10^5$ 我 ...

  7. JS常用工具函数(持续记录)

    1.设置获取cookie //方式1 //设置cookie function SetCookie(name, value)//两个参数,一个是cookie的名字,一个是值 { var Days = 3 ...

  8. 【liferay】5、使用PortletURL进行跨portlet通信 liferay6.2

    [问题] 1.当我们一个页面存在多一个portlet的时候,如在不同的portlet之间传参? [解决办法] 1.在liferay官方有几种方式,比较复杂麻烦,不是太实用,这里不再赘述. 2.通过fr ...

  9. Python numpy 中 keepdims 的含义

    keepdims主要用于保持矩阵的二维特性 import numpy as np a = np.array([[1,2],[3,4]]) # 按行相加,并且保持其二维特性 print(np.sum(a ...

  10. opencv2函数学习之erode、dilate:图像腐蚀和膨胀

    图像腐蚀和图像膨胀是图像中两种最基本形态学操作. ,-), ,int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphol ...