一.什么是外观模式?

简单的说,外观模式是用来简化接口的。

通常,我们觉得一个子系统不好用,可能是因为它提供的外部接口太接近低层组件,让我们用起来感到很麻烦。

因为我们不需要知道内部细节,我们只想要一个“一键完成”功能,调用子系统的某个方法,它可能替我们完成了图片预处理,而不是靠我们自己来调用灰度化方法、图像增强算法、噪声处理方法等等来一步步实现预处理。

如果子系统提供的接口太接近低层组件,不仅不易用,而且破坏了子系统的封装(想调用子系统就必须了解其各个低层组件,我们被迫知道了太多不应该知道的细节。。)

二.举个例子

假设有一个封装好的老式洗衣机,它提供了这些对外接口:

package FacadePattern;

/**
* @author ayqy
* 定义洗衣机接口
*/
public interface Washer {
/*
* 公共部分
* */
//连接电源
public abstract boolean connectToPower(); /*
* 洗涤部分
* */
//打开左侧的洗涤舱
public abstract void openLeftSide();
//打开注水口
public abstract void openWaterHole();
//开始注水
public abstract void startWaterInjection();
//停止注水
public abstract void stopWaterInjection();
//开始旋转左侧洗涤舱
public abstract void startWashing();
//停止旋转左侧洗涤舱
public abstract void stopWashing(); /*
* 脱水部分
* */
//打开右侧的脱水舱
public abstract void openRightSide();
//开始旋转右侧脱水舱
public abstract void startDewatering();
//停止旋转右侧脱水舱
public abstract void stopDewatering(); /*
* 排水部分省略。。
* */
}

没办法,它实在太老了,不能满足我们的现代生活,但我们又买不起新的自动化洗衣机,所以我们需要把它变成一个半自动的洗衣机,用节省下来的时间去写代码。。

先看看我们是如何用老式洗衣机来洗衣服的:

package FacadePattern;

/**
* @author ayqy
* 使用老式洗衣机来洗衣服
*/
public class Washing implements Washer{ public static void main(String[] args) {
//创建洗衣机对象
Washer washer = new Washing();
//连接电源
if(washer.connectToPower()){
//打开洗涤舱
washer.openLeftSide();
/*装入脏衣服过程省略*/
//打开注水口
washer.openWaterHole();
//开始注水
washer.startWaterInjection();
/*等待5分钟*/
//停止注水
washer.stopWaterInjection();
/*添加洗涤剂过程省略*/
//开始洗涤
washer.startWashing();
/*15分钟后停止*/
washer.stopWashing(); /*
* 脱水部分省略
* */
}
} /*
* 忽略下面自动生成的这些东西。。
* */
@Override
public boolean connectToPower() {
// TODO Auto-generated method stub
return false;
} @Override
public void openLeftSide() {
// TODO Auto-generated method stub } @Override
public void openWaterHole() {
// TODO Auto-generated method stub } @Override
public void startWaterInjection() {
// TODO Auto-generated method stub } @Override
public void stopWaterInjection() {
// TODO Auto-generated method stub } @Override
public void startWashing() {
// TODO Auto-generated method stub } @Override
public void stopWashing() {
// TODO Auto-generated method stub } @Override
public void openRightSide() {
// TODO Auto-generated method stub } @Override
public void startDewatering() {
// TODO Auto-generated method stub } @Override
public void stopDewatering() {
// TODO Auto-generated method stub } }

仅仅演示了一个洗衣服的过程,我们就调用了那么多操作,而且我们必须知道这台洗衣机的内部细节,不然根本无法使用它。。

洗衣服可能需要60分钟(注水 + 洗涤 + 脱水 + 排水),在这期间我们几乎什么事情也做不了,只能蹲在洗衣机旁边不停的操作机器

-------

我想,我们可能需要一个遥控器,上面有2个按钮:

  • 洗涤
  • 脱水

然后我们洗衣服的过程会变成这样:

  1. 摁洗涤按钮,自动连接电源,自动打开洗涤舱,自动注水5分钟,自动开始洗涤15分钟
  2. 摁脱水按钮,自动打开脱水舱,自动脱水10分钟,自动排水,自动断开电源

这简直太棒了,我们可以在吃午餐前摁一下洗涤按钮,吃完之后去把衣服拿到右侧,再摁一下脱水按钮,然后去上班,下午回来之后把衣服晾起来就好了

(当然,手动把衣服从左侧拿到右侧的过程是避免不了的,毕竟它太老了,想变成全自动洗衣机是不可能的。。)

来看看我们自己做的遥控器:

package FacadePattern;

/**
* @author ayqy
* 遥控器(也就是所谓的外观)
*/
public class WasherFacade {
private Washer washer; public WasherFacade(Washer washer){
this.washer = washer;
} /**
* 自动洗涤
*/
public void washing(){
//连接电源
if(washer.connectToPower()){
//打开洗涤舱
washer.openLeftSide();
/*装入脏衣服过程省略*/
//打开注水口
washer.openWaterHole();
//开始注水
washer.startWaterInjection();
/*等待5分钟*/
//停止注水
washer.stopWaterInjection();
/*添加洗涤剂过程省略*/
//开始洗涤
washer.startWashing();
/*15分钟后停止*/
washer.stopWashing();
}
} /**
* 自动脱水
*/
public void dewashing(){
/*判断是否已连接电源过程省略*/
//打开右侧的脱水舱
washer.openRightSide();
//开始旋转右侧脱水舱
washer.startDewatering();
//停止旋转右侧脱水舱
washer.stopDewatering(); /*
* 排水过程省略
* 切断电源过程省略
* */
}
}

有了遥控器之后,我们是这样洗衣服的:

package FacadePattern;

/**
* @author ayqy
* 测试应用了外观模式的半自动洗衣机(利用遥控器)
*/
public class Test implements Washer{ public static void main(String[] args) {
//创建老式洗衣机
Washer washer = new Test();
//创建外观(遥控器)
WasherFacade facade = new WasherFacade(washer);
//按下洗涤按钮开始洗衣服
facade.washing();
/*把衣服拿到另一侧*/
//按下脱水按钮开始脱水
facade.dewashing();
} /*
* 忽略下面这些自动生成的东西。。
* */
@Override
public boolean connectToPower() {
// TODO Auto-generated method stub
return false;
} @Override
public void openLeftSide() {
// TODO Auto-generated method stub } @Override
public void openWaterHole() {
// TODO Auto-generated method stub } @Override
public void startWaterInjection() {
// TODO Auto-generated method stub } @Override
public void stopWaterInjection() {
// TODO Auto-generated method stub } @Override
public void startWashing() {
// TODO Auto-generated method stub } @Override
public void stopWashing() {
// TODO Auto-generated method stub } @Override
public void openRightSide() {
// TODO Auto-generated method stub } @Override
public void startDewatering() {
// TODO Auto-generated method stub } @Override
public void stopDewatering() {
// TODO Auto-generated method stub } }

简直轻松惬意,不过仔细一看,不就是定义了一个方法来封装方法调用嘛,有什么了不起的?与在我们的新项目代码中建立定义两个方法负责洗涤和脱水有什么区别吗?

当然有,不要着急

三.外观模式的优点

1.低耦合

先看看我们的命名方式,遥控器叫做WasherFacade,如果要划分模块,它应该与Washer放在一起吧

没错,通过定义Facade,我们成功解耦了Washer与我们的代码,意味着一旦Washer发生变更,我们直接修改Facade就好了,而不是在我们冗长的项目代码里寻找某两个方法

2.保护了子系统的封装

我们并没有打开封装好的Washer去修改,而是添加了一些代码来简化Washer的接口,以前调用者对Washer的内部很了解,但现在它对Washer几乎一无所知(除构造方法外)

3.适用于含有多个不同对象的子系统

例子中的Washer只是一个单一对象,好像应该由Washer本身提供这样的简单接口

但如果要实现日常起居的半自动化,我们会面对多个对象,比如门,窗,窗帘,电视,微波炉,洗衣机,电脑等等

我们希望一键准备早餐(自动开灯,自动开启微波炉加热),一键午睡(自动关门,自动拉上窗帘,自动熄灭灯光)等等功能,外观模式同样适用:

我们只需要让外观多持有几个具体对象就好了

4.有效地简化了子系统的接口

之前洗衣服需要蹲在洗衣机旁不停的操作,现在我们可以“一键完成”了,这才是我们想要的简单易用的接口

5.满足“最少知识原则”

我们做到了“只和朋友交谈”,我们的新系统只认识Facade,只和它交谈,而不是跑去和子系统中的一大堆低层组件交谈

四.总结

外观模式,用来为复杂的子系统提供简单易用的高层接口。

当你纠结于很多低层组件得不到解脱的时候,不妨去做一个遥控器,我想,你可能确实需要它。。

设计模式之外观模式(Facade Pattern)的更多相关文章

  1. 乐在其中设计模式(C#) - 外观模式(Facade Pattern)

    原文:乐在其中设计模式(C#) - 外观模式(Facade Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 外观模式(Facade Pattern) 作者:webabcd 介绍 ...

  2. 二十四种设计模式:外观模式(Facade Pattern)

    外观模式(Facade Pattern) 介绍为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用.示例有一个Message实体类,某对象对它 ...

  3. python : 设计模式之外观模式(Facade Pattern)

    #为啥要用外观模式举例说明 这个例子很形象,直接从人家博客上贴过来的,参考链接在下面 不知道大家有没有比较过自己泡茶和去茶馆喝茶的区别,如果是自己泡茶需要自行准备茶叶.茶具和开水,如图1(A)所示,而 ...

  4. 【UE4 设计模式】外观模式 Facade Pattern

    概述 描述 外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用.外观模式又称为门面模式,它是一 ...

  5. 设计模式系列之外观模式(Facade Pattern)——提供统一的入口

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  6. 使用C# (.NET Core) 实现适配器模式 (Adapter Pattern) 和外观模式 (Facade Pattern)

    本文的概念内容来自深入浅出设计模式一书 现实世界中的适配器(模式) 我带着一个国标插头的笔记本电脑, 来到欧洲, 想插入到欧洲标准的墙壁插座里面, 就需要用中间这个电源适配器. 面向对象的适配器 你有 ...

  7. 8.4 GOF设计模式三: 外观模式 Facade

    GOF设计模式三: 外观模式 Facade  “现有系统”功能强大.复杂,开发“新系统”需要用到其中一部分,但又要增加一部 分新功能,该怎么办?4.1 Facade Pattern: Key Fea ...

  8. 设计模式(八): 从“小弟”中来类比"外观模式"(Facade Pattern)

    在此先容我拿“小弟”这个词来扯一下淡.什么是小弟呢,所谓小弟就是可以帮你做一些琐碎的事情,在此我们就拿“小弟”来类比“外观模式”.在上面一篇博文我们完整的介绍了“适配器模式”,接下来我们将要在这篇博客 ...

  9. C#设计模式——外观模式(Facade Pattern)

    一.概述 在系统设计中,某一个系统可能非常庞大,用户要使用该系统就不得不掌握大量的接口,造成使用的不便.这时可以考虑将该系统细分成一系列子系统并使子系统间的耦合降到最低,利用外观模式提供一个外观对象, ...

  10. 外观模式Facade pattern

    http://www.runoob.com/design-pattern/facade-pattern.html 外观模式 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一 ...

随机推荐

  1. Python3 open() 函数

    Python3 open() 函数  Python3 内置函数 Python open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 ...

  2. PAT L2-005 集合相似度(模拟集合set)

    给定两个整数集合,它们的相似度定义为:Nc/Nt*100%.其中Nc是两个集合都有的不相等整数的个数,Nt是两个集合一共有的不相等整数的个数.你的任务就是计算任意一对给定集合的相似度. 输入格式: 输 ...

  3. centos7.5配置双网卡上网

    一.环境及说明 当初有这个需求,主要是帮一个高校的客户搭建一个大数据集群,使用的是校园网,交换机上一个端口只能连接一部电脑上网,不能通过路由组建子网,确保集群中的服务器有子网ip的同时,也能够通过公网 ...

  4. 每天学一点儿HTML5的新标签

    sections部分 (http://www.w3.org/TR/html5/sections.html) 标签:article article标签用来表示页面中一段完整的可以自我包含的片段,具有可重 ...

  5. redis中multi和pipeline区别以及效率(推荐使用pipeline)

    手册得知 pipeline 只是把多个redis指令一起发出去,redis并没有保证这些指定的执行是原子的:multi相当于一个redis的transaction的,保证整个操作的原子性,避免由于中途 ...

  6. 用python做数值计算

    http://sebug.net/paper/books/scipydoc/scipy_intro.html http://www.cnblogs.com/weilq/p/3432817.html h ...

  7. maven 发布打包部署 命令

    一.配置好jdk 二.下载安装maven http://maven.apache.org/download.cgi 三.添加环境变量 1. 添加 M2_HOME 和 MAVEN_HOME 环境变量到 ...

  8. npm 如何设置镜像站为淘宝网

    转载 2015年06月24日 17:12:12 10542 淘宝镜像:http://npm.taobao.org/ 镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候 ...

  9. 数据分页c#

    存储过程分页的全套代码aspx页面的代码using System;using System.Collections.Generic;using System.Linq;using System.Web ...

  10. msys2 显示git branch

    在.bashrc或.bash_profile中添加以下内容 function parse_git_branch () { git branch 2> /dev/null | sed -e '/^ ...