门面(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 ...
随机推荐
- Linux系统date命令的参数及获取时间戳的方法
date指令相关用法示例 date 用法: date [OPTION]... [+FORMAT]date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] ...
- WEB开发性能优化--核心定义介绍篇(1)
推荐理由 随着 互联网的蓬勃发展,并且伴随着产品功能的越来越复杂,对于技术人员来说最大的挑战就是如何在保证业务快速发展的同时,也可保证不断复杂的业务对用户体验的影响,其中对用户来说最重要的体验指标是如 ...
- 原生JS实现弹出窗口的拖拽
上一篇说了一下弹出窗口功能的实现思路,一般情况下紧接着就会需要做到弹窗的移动,当然现在有很插件.库比如hammer可以使用,效率也非常好.但我觉得还是有必要了解一下原生JS的实现思路及方式,如下: 思 ...
- Python3.6安装
1.安装依赖包#yum install -y openssl openssl-devel openssl-static#yum groupinstall -y "Development to ...
- Apache优化
控制MaxClients的设置,以避免服务器产生太多的子进程而发生交换.进程间的数据交换会占用很大内存,数据交换产生的滞后使用户总感觉"不够快",所以用户就可能去按"停止 ...
- [SinGuLaRiTy] 2017-03-27 综合性测试
[SinGuLaRiTy-1013] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 这是 三道 USACO 的题...... 第一题:奶牛飞 ...
- 如何在IDEA中调试 Jar文件
原创文章,转载请注明出处:http://www.cnblogs.com/acm-bingzi/p/6668333.html 问题: 一般情况下,可以打成Jar包的项目,它的源码运行Applicat ...
- JavaWeb总结(七)—JSTL标签库
一.JSTL标签库介绍 JSTL标签库的使用时为了弥补HTML标签的不足,规范自定义标签的使用而诞生的.使用JSTL标签的目的是不希望在JSP中出现Java逻辑代码. 二.JSTL标签库的分类 1.核 ...
- 网站启用gzip压缩
gzip压缩启用不启用还是要看实际情况的,启用gzip后可以相应的减轻带宽压力但是同时也会增加cpu的压力(压缩解压),相反的如果不启用那么cpu压力也会相应的减少,具体情况具体分析. Linux开启 ...
- Android开发事件总线之EventBus运用和框架原理深入理解
[Android]事件总线之EventBus的使用背景 在我们的android项目开发过程中,经常会有各个组件如activity,fragment和service之间,各个线程之间的通信需求:项目中用 ...