门面(Facade)模式
门面模式是对象的结构模式,外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
医院的例子
现代的软件系统都是比较复杂的,设计师处理复杂系统的一个常见方法便是将其“分而治之”,把一个系统划分为几个较小的子系统。如果把医院作为一个子系统,按照部门职能,这个系统可以划分为挂号、门诊、划价、化验、收费、取药等。看病的病人要与这些部门打交道,就如同一个子系统的客户端与一个子系统的各个类打交道一样,不是一件容易的事情。首先病人必须先挂号,然后门诊。如果医生要求化验,病人必须首先划价,然后缴费,才可以到化验部门做化验。化验后再回到门诊室。
上图描述的是病人在医院里的体验,图中的方框代表医院。解决这种不便的方法便是引进门面模式,医院可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。这个接待员就是门面模式的体现,病人只接触接待员,由接待员与各个部门打交道。
门面模式的结构
门面模式没有一个一般化的类图描述,最好的描述方法实际上就是以一个例子说明。
由于门面模式的结构图过于抽象,因此把它稍稍具体点。假设子系统内有三个模块,分别是ModuleA、ModuleB和ModuleC,它们分别有一个示例方法,那么此时示例的整体结构图如下:
在这个对象图中,出现了两个角色:
- 门面(Facade)角色 :客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
- 子系统(SubSystem)角色 :可以同时有一个或者多个子系统。每个子系统都不是一个单独的类,而是一个类的集合(如上面的子系统就是由ModuleA、ModuleB、ModuleC三个类组合而成)。每个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。
Facade类其实相当于A、B、C模块的外观界面,有了这个Facade类,那么客户端就不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在,客户端只需要跟Facade类交互就好了,从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统。
门面模式的实现
使用门面模式还有一个附带的好处,就是能够有选择性地暴露方法。一个模块中定义的方法可以分成两部分,一部分是给子系统外部使用的,一部分是子系统内部模块之间相互调用时使用的。有了Facade类,那么用于子系统内部模块之间相互调用的方法就不用暴露给子系统外部了。
比如,定义如下A、B、C模块。
/* @类说明 :
*/
public class ModuleA {
/**
* 提供给子系统外部使用的方法
*/
public void a1() {
System.out.println("ModuleA.a1()");
};
public void a2() {
};
public void a3() {
};
} /**
* @类说明 :
*/
public class ModuleB {
/**
* 提供给子系统外部使用的方法
*/
public void b1() {
System.out.println("ModuleB.b1()");
};
public void b2() {
};
public void b3() {
};
} /**
* @类说明 :
*/ public class ModuleC {
/**
* 提供给子系统外部使用的方法
*/
public void c1() {
System.out.println("ModuleC.c1()");
};
public void c2() { };
public void c3() { };
}
门面类的实现:
/**
* @类说明 :
*/
public class ModuleFacade {
private ModuleA a = new ModuleA();
private ModuleB b = new ModuleB();
private ModuleC c = new ModuleC();
/**
* 下面这些是A、B、C模块对子系统外部提供的方法
*/
public void a1() {
a.a1();
}
public void b1() {
b.b1();
}
public void c1() {
c.c1();
}
}
View Cod
客户端实现
/**
* @类说明 :
*/
public class Client {
public static void main(String[] args) {
ModuleFacade facade = new ModuleFacade();
facade.a1();
facade.b1();
facade.c1();
}
}
这样定义一个ModuleFacade类可以有效地屏蔽内部的细节,免得客户端去调用Module类时,发现一些不需要它知道的方法。比如a2()和a3()方法就不需要让客户端知道,否则既暴露了内部的细节,又让客户端迷惑。对客户端来说,他可能还要去思考a2()、a3()方法用来干什么呢?其实a2()和a3()方法是内部模块之间交互的,原本就不是对子系统外部的,所以干脆就不要让客户端知道。
一个系统可以有几个门面
在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统都有一个门面类,整个系统可以有数个门面类。
为子系统增加新行为
初学者往往以为通过继承一个门面类便可在子系统中加入新的行为,这是错误的。门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。比如医院中的接待员并不是医护人员,接待员并不能为病人提供医疗服务。
如果一个门面模式不能将子系统的所有的行为提供给外界,那么可以通过修改门面类或继承门面类的方法,使门面类或子类将子系统的行为提供给外界.但是如果一个子系统没有某个行为,想通过修改门面类或继承门面类的方法来提供这种新行为是错误的.
门面模式的优点:
- 松散耦合。门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。
- 简单易用。门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。
- 更好的划分访问层次。通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。
门面(Facade)模式的更多相关文章
- 十一、外观(Facade)模式--结构模式(Structural Pattern)
外部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行,这就是门面模式.门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行. 门面模式提供一个高层次 ...
- 转:Ogre源码分析之Root类、Facade模式
Ogre源码分析(一)Root类,Facade模式 Ogre中的Root对象是一个Ogre应用程序的主入口点.因为它是整个Ogre引擎的外观(Façade)类.通过Root对象来开启和停止Ogre是最 ...
- PHP重载以及Laravel门面Facade
目录 重载的概念 魔术方法中的重载 属性重载 方法重载 Laravel中的Facade 扩展 谈谈__invoke Laravel提供了许多易用的Facade,让我们用起来特步顺手,那么这些Facad ...
- Laravel开发:Laravel框架门面Facade源码分析
前言 这篇文章我们开始讲 laravel 框架中的门面 Facade,什么是门面呢?官方文档: Facades(读音:/fəˈsäd/ )为应用程序的服务容器中可用的类提供了一个「静态」接口.Lara ...
- Python笔记:设计模式之facade模式
facade模式,即门面模式,也称外观模式,这个模式的核心思想是使用facade对象为外部客户端提供一个统一的访问一组子系统的接口,即客户端不会直接与各个子系统交互,而是通过facade对象与各个子系 ...
- C++设计模式-Facade模式
Facade模式 作用:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 动机 将一个系统划分成为若干个子系统有利于降低系统的复杂性.一 ...
- 外观模式/facade模式/结构型模式
外观模式 为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式三要素(client-facade-subSystem) 外观角色 ...
- 设计模式--外观(Facade)模式
Insus.NET在去年有写过一篇<软件研发公司,外观设计模式(Facade)>http://www.cnblogs.com/insus/archive/2013/02/27/293606 ...
- Facade模式
Facade模式要求一个子系统的外部与其内部的通信必须通过一个统一的Facade对象进行.Facade模式提供一个高层次的接口,使得子系统更易于使用. 就如同医院的接待员一样,Facade模式的Fa ...
随机推荐
- dede织梦数据表字段解释
提示:常用字段,可以在dede后台->系统->SQL命令行工具,执行sql语句来批量修改 dede_addonarticle 附加文章表 aid int(11) 文章编号 ...
- Linux ubuntu 安装gcc、g++、 pcre、zlib、ssl、nginx和该内存不能为written解决方法
1.楼主也是第一次接触Linux 如果有错误的地方还请各位朋友指出.... 2.gcc.g++依赖库:sudo apt-get install build-essential,sudoapt-get ...
- js获取url中的参数方法
直接调用函数即可,函数如下: function getURLParam(name) { return decodeURIComponent((new RegExp('[?|&]' + name ...
- 关于压缩jar包时提示*.*没有这个文件或目录的问题以及解决办法:
关于压缩jar包时提示.没有这个文件或目录的问题以及解决办法: 问题描述: 我在打包jar时,CMD中进入到包的上一层目录. 在命令提示符中输入 提示如下: 从提示中可知没有找到我们想要打包的clas ...
- ReactJS入门3:组件的生命周期
本文主要介绍组件的生命周期. 组建的生命周期主要分为3个:Mounting.Updating.Unmounting. 1. Mounting:组件被加载到DOM 在本阶段,主要有三个方法: 1 ...
- Xcode的Architectures、Valid Architectures和Build Active Architecture Only属性(原创)
最近xcode升级了5.1版本,升级之后程序报关于要适配arm64机器的错.之前对xcode的参数配置,一直不是很了解,但实现先面对问题了,就调查了一下并解决它. 一个一个来吧. Architectu ...
- jquery的冒泡事件event.stopPropagation()
js中的冒泡事件与事件监听 冒泡事件 js中“冒泡事件”并不是能实际使用的花哨技巧,它是一种对js事件执行顺序的机制,“冒泡算法”在编程里是一个经典问题,冒泡算法里面的冒泡应该 说是交换更加准确:js ...
- Spark名词解释及关系
随着对spark的业务更深入,对spark的了解也越多,然而目前还处于知道的越多,不知道的更多阶段,当然这也是成长最快的阶段.这篇文章用作总结最近收集及理解的spark相关概念及其关系. 名词 dri ...
- Druid Indexing 服务
索引服务由三个主要组件:一个是peon 组件,可以运行一个任务,一个是Middle Managers组件,管理peons,和一个overlord 组件管理任务分发给Middle Managers. o ...
- 【one day one linux】好用的数据处理工具awk
awk:好用的数据处理工具 取自<鸟哥私房菜>awk一节 应用:awk是以一行为一次的处理单位,将一行分成数个“字段”进行处理. #awk的命令格式 awk '条件类型1{动作1} 条件类 ...