一.什么是外观模式?

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

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

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

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

二.举个例子

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

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. hdoj1043 Eight(逆向BFS+打表+康拓展开)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 思路: 由于自己对康拓展开用的太少,看到这个题没想到康拓展开,最开始打算直接转换为数字,但太占内 ...

  2. mysql增加远程连接用户及查看数据库表结构

    一.增加远程连接用户 1.用root权限登录数据库  2.加用户:grant all privileges on *.* to '111'@'192.168.1.%' identified by '2 ...

  3. debug、release

    1.区别 Debug 和 Release 并没有本质的区别,他们只是VC预定义提供的两组编译选项的集合,编译器只是按照预定的选项行动.如果我们愿意,我们完全可以把Debug和Release的行为完全颠 ...

  4. Windows系统之hosts文件

    对于Hosts文件相信很多Win7的系统用户会比较陌生,其实Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,所以功 ...

  5. 单词拆分 I · Word Break

    [抄题]: 给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词. s = "lintcode" dict = ["lint" ...

  6. Web Components 规范学习

    最新的规范在这里:http://w3c.github.io/webcomponents/explainer/ 依据规范,有以下四个组成部分: Templates Custom Elements Sha ...

  7. js strict 关键字

    strict strict模式,JavaScript在设计之初,为了方便初学者学习,并不强制要求用var申明变量.这个设计错误带来了严重的后果:如果一个变量没有通过var申明就被使用,那么该变量就自动 ...

  8. Spring框架的JDBC模板技术概述

    1. Spring框架中提供了很多持久层的模板类来简化编程,使用模板类编写程序会变的简单 2. 提供了JDBC模板,Spring框架提供的 * JdbcTemplate类 3. Spring框架可以整 ...

  9. C#控制台自定义背景颜色,字体颜色大全

    效果: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Syst ...

  10. 如何定义一个高逼格的原生JS插件

    插件的需求 我们写代码,并不是所有的业务或者逻辑代码都要抽出来复用.首先,我们得看一下是否需要将一部分经常重复的代码抽象出来,写到一个单独的文件中为以后再次使用.再看一下我们的业务逻辑是否可以为团队服 ...