写在前面

为方便读者,本文已添加至索引

Facade(外观)模式定义了一个高层接口,它能为子系统中的一组接口提供一个一致的界面,从而使得这一子系统更加容易使用。欢迎回到时の魔导士的魔法世界。在对战坏女巫的魔法生物一役中(见Bridge模式笔记),白雪公主获得大逆转胜利机会的关键是附魔武器的诞生。但是,普通的武器工坊(见FactoryMethod模式笔记)生产不了附魔武器,只能是通过特殊的附魔工坊获得。经过这一战之后,大家也觉得除了武器,还需要能保护身体的护甲来抵挡伤害。因此,霍比特人们又打算建立一些护甲工坊。如此一来,用于生产战斗用品的系统就越来越多了。小霍比特人们如果需要获得一整套适合自己的战斗用品,就不得不分别调用每个系统的生产方法。事实上,小霍比特人们并不关心每个系统具体的工作细节,他们只想要得到一件成品而已。对于他们而言,这些生产系统中那些功能强大但层次较低的接口只会使他们的任务复杂化。

为了让小霍比特人们的生活、战斗更便利,好女巫格琳达(Glinda原型请参见《魔境仙踪》,当然,你不会忘记一点:我肯定不会忠于原著)为他们提供了一个高层接口,并且对他们屏蔽了这些生产系统类——格琳达的小屋,在森林里面正式开业了。于是小霍比特人们只需要来到小屋中,告诉Glinda自己需要什么,再付上一点点小酬劳,就可以得到想到的东西啦。

那这一切跟我们今天笔记的主题有什么关系呢?其实这正是Facade模式一个例子,我们将在示例分析部分更加详细地探讨这个问题。首先还是让我们快速地熟悉下什么是Facade模式吧!

要点梳理

  • 目的分类

    • 对象结构型模式
  • 范围准则
    • 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
  • 主要功能
    • 为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  • 适用情况
    • 当我们要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。
    • 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
    • 当我们需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,我们可以让它们仅通过Facade进行通讯,从而简化了它们之间的依赖关系。
  • 参与部分
    • Facade:它知道哪些子系统类负责处理请求;它将客户的请求代理给适当的子系统对象。
    • Subsystem Classes:实现子系统的各种功能;处理由Facade对象指派的任务;不会有Facade的任何相关信息。
  • 协作过程
    • 客户程序通过发送请求给Facade的方式与子系统通讯, Facade将这些消息转发给适当的子系统对象。尽管是子系统中的有关对象在做实际工作,但Facade模式本身也必须将它的接口转换成子系统的接口。
    • 使用Facade的客户程序不需要直接访问子系统对象。
  • 结构图

示例分析 - 格琳达的小屋

在我们前来参观格琳达的小屋之前,不妨先回顾一下那些生产战斗用品的子系统,从而能更好地理解Facade模式工作的机制。由于在Bridge模式笔记中提到Weapon类的设计架构发生了改变,因此包括普通的武器工坊在内的设计也相应地进行了调整。

首先是我们基本的Weapon类:

 class Weapon : public VisualObject {
public:
Weapon(int);
~Weapon() { delete _impl; }
// ... other ...
protected:
WeaponImpl* getWeaponImpl();
private:
WeaponImpl* _impl;
} Weapon::Weapon(int type) {
switch(type)
{
case WEAPON_SWORD:
_impl = new SwordImpl();
break;
case WEAPON_SHIELD:
_impl = new ShieldImpl();
break;
case WEAPON_BOW:
_impl = new BowImpl();
break;
// ... other cases ...
default:
break;
}
}

Weapon

还有调整后的WeaponFactory类以及它的子类们:

 class WeaponFactory {
public:
virtual Weapon* createWeapon() = ;
} class HonorOfFighter : public WeaponFactory {
public:
HonorOfFighter();
Weapon* createWeapon() { return new Weapon(WEAPON_SWORD); }
} class BliefOfDefender : public WeaponFactory {
public:
BliefOfDefender();
Weapon* createWeapon() { return new Weapon(WEAPON_SHEILD); }
} class PrecisionOfHunter : public WeaponFactory {
public:
PrecisionOfHunter();
Weapon* createWeapon() { return new Weapon(WEAPON_BOW); }
} // ... other weapon factories ...

WeaponFactory

以及前文中提到的附魔工坊:

 #define ENCHANTED_ICE       1
#define ENCHANTED_FIRE 2
#define ENCHANTED_SHOCK 3 class EnchantedWeaponFactory {
public:
Weapon* createEnchantedWeapon(int, int);
// ... other ...
} EnchantedWeaponFactory::createEnchantedWeapon(int element, int type) {
return new EnchantedWeapon(element, type);
}

EnchantedWeaponFactory

暂时还没有建成的护甲工坊:

 class ArmorFactory : public VisualObject {
public:
virtual Armor* createArmor() = ;
}

ArmorFactory

好啦,该去参观参观格琳达的小屋HouseOfGlinda了:

 #define NOTHING    0

 class HouseOfGlinda {
public:
VisualObject* onSale(int, int); // a simple interface. // ... It's a Singleton ...
static HouseOfGlinda* getInstance() {
if (_instance == ) {
_instance = new HouseOfGlinda();
} return _instance;
} protected:
Weapon* getWeapon(int);
Weapon* getEnchantedWeapon(int, int);
  // ... Armor will be comming soon ...
private:
HouseOfGlinda();
static HouseOfGlinda* _instance;
} VisualObject* HouseOfGlinda::onSale(int type, int info) {
if (WEAPON_SWORD == type ||
WEAPON_SHIELD == type ||
WEAPON_BOW == type
// ... other weapon ...
  ) { if (NOTHING == info) {
return getWeapon(type);
}
else {
return getEnchantedWeapon(type, info);
}
}
// else for armor ...
} Weapon* HouseOfGlinda::getWeapon(int type) {
WeaponFacotry* factory = ;
Weapon* weapon = ;
switch(type) {
case WEAPON_SWORD:
factory = new HonorOfFighter();
break;
case WEAPON_SHIELD:
factory = new BliefOfDefender();
break;
// ... other cases ...
default:
break;
}
weapon = factory->createWeapon();
if (factory) delete factory;
return weapon;
} Weapon* HouseOfGlinda::getEnchantedWeapon(int type, int info) {
EnchantedWeaponFactory factory;
return factory.createEnchantedWeapon(info, type);
}

注意到HouseOfGlinda类中,onSale接口对客户屏蔽了子系统中的组件(譬如说WeaponFactory以及EnchantedWeaponFactory,甚至还在建设当中的ArmorFactory),作为客户的小霍比特人们只需要发送请求给HouseOfGlinda对象,它就能将消息转发给相应的子系统去处理,比方说,有人想要一把普通的剑,而另一个人想要一面冒着火焰的盾牌:

 VisualObject* obj1 = HouseOfGlinda::getInstance->onSale(WEAPON_SHIELD, ENCHANTED_FIRE);
VisualObject* obj2 = HouseOfGlinda::getInstance->onSale(WEAPON_SWORD, NOTHING);

瞧,多方便呀。此外,如果真有需要,小霍比特人还是可以直接访问工坊从而获得私人定制的武器。当然通过格琳达的小屋会更加方便,这样他们有更多时间去享受啤酒和美食的乐趣了。

一张简单的UML图:

特点总结

Facade模式简单且易于理解,对于我们来说也很常用。它的优点如下:

  1. 它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
  2. 它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。Facade模式可以消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要。
  3. 如果应用需要,它并不限制它们使用子系统类。因此我们可以在系统易用性和通用性之间加以选择。

写在最后

今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!

[学习笔记]设计模式之Facade的更多相关文章

  1. [学习笔记]设计模式之Composite

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...

  2. [学习笔记]设计模式之Abstract Factory

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...

  3. [学习笔记]设计模式之Builder

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构.但是,工作一段时间之后,自己渐渐意识到,哪怕是自己 ...

  4. [学习笔记]设计模式之Adapter

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...

  5. [学习笔记]设计模式之Bridge

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...

  6. [学习笔记]设计模式之Prototype

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔 ...

  7. [学习笔记]设计模式之Command

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...

  8. [学习笔记]设计模式之Chain of Responsibility

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...

  9. [学习笔记]设计模式之Proxy

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...

随机推荐

  1. HTML5与CSS3权威指南.pdf8

    第17章 与背景和边框相关的样式 与背景相关的新增属性 background-clip指定背景的显示范围 background-origin指定绘制背景图像时的起点 background-size指定 ...

  2. 基于UDP协议的控制台聊天

    这几天学了java的网络编程弄出一个基于UDP协议的聊天工具 功能 添加并且备注好友(输入对方的ip) 删除好友 查看好友列表 用java写的控制台程序导出可执行程序后不能双击打开 还需要些一个脚本文 ...

  3. 基于zabbix 内置key的应用

    一.内置key说明: Zabbix 内置了很多丰富的key,使得咱们再添加linux os模板的时候,已经帮我们把key给定义好,这样我们就能够直接链接模板就可以使用了. 我们这边的话列举一些内置ke ...

  4. apache启动目录(禁止目录)与设置默认入口文件的方法

    设置默认入口文件的方法: 打开apache的conf目录,找到httpd.conf文件,打开这个文件,搜索dir_module,找到以下截图修改位置进行修改,注意重启apache服务器,修改位置才会生 ...

  5. LTTng 简介&使用实战

    一.LTTng简介 LTTng: (Linux Trace Toolkit Next Generation),它是用于跟踪 Linux 内核.应用程序以及库的系统软件包.LTTng 主要由内核模块和动 ...

  6. [Webpack 2] Chunking common modules from multiple apps with the Webpack CommonsChunkPlugin

    If you have a multi-page application (as opposed to a single page app), you’re likely sharing module ...

  7. [Javascript] Immutable opreators

    slice filter map ...spread concat Object.assign let items = [1,2,3]; //add let newItems = [...items, ...

  8. Java IO流学习总结(转)

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  9. kernel笔记:TCP参数

    http://blog.chinaunix.net/uid-27119491-id-3346430.html 本文将介绍网络连接建立的过程.收发包流程,以及其中应用层.tcp层.ip层.设备层和驱动层 ...

  10. 关于cocos2d-x精灵加亮及变灰效果

    //根据现有CCSprite,变亮和变灰 static CCSprite* graylightWithCCSprite(CCSprite* oldSprite,bool isLight) { //CC ...