写在前面

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

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. vmware10安装win8x64(亲测)

    首先,创建虚拟机,选择典型 下一步后 弄完后选择“完成”,这下就可以安装了 之后分区,之后选cdrom做启动,之后安装win8到第一分区

  2. 开发者MAC电脑里的十八般兵器

    古人常以刀.枪.剑.戟.斧.钺.铲.叉.鞭.锏.锤.戈.镋.棍.槊.棒.矛.钯十八种兵器,样样精通,来形容一个人的武学技能get状态.在开发者的世界里,熟练掌握各种辅助工具,可以达到事半功倍,快速提高 ...

  3. typeof和instanceof 运算符

    instanceof运算符与typeof运算符相似,用于识别正在处理的对象的类型,但是在使用 typeof 运算符时采用引用类型存储值会出现一个问题. 无论引用的是什么类型的对象,它都返回 " ...

  4. java利用commons-email发送邮件并进行封装

    本例中利用commons-email发送邮件并进行封装,支持html内容和附件:Commons Email是Apache的Commons子项目下的一个邮件客户端组件,它是基于JavaMail的,大大简 ...

  5. CodeIgniter开发实际案例-新闻网站【转】

    CodeIgniter开发实际案例-新闻网站 转:http://blog.csdn.net/ict2014/article/details/22104711?utm_source=tuicool&am ...

  6. Cas Server中各配置文件介绍

    Cas Server中所有的配置文件都是放在WEB-INF目录及其子目录下的. 在WEB-INF/classes下的配置文件有: l  cas-theme-default.properties:该文件 ...

  7. gitbook 制作 beego 参考手册

    安装gitbook工具 npm install -g gitbook-cli 从github 下载beego文档 https://github.com/beego/beedoc 创建目录 在 zh-c ...

  8. 百度UEditor开发案例(JSP)

    本案例的开发环境:MyEclipse+tomcat+jdk     本案例的开发内容: 用百度编辑器发布新闻(UEditor的初始化开发部署) 编辑已发过的新闻(UEditor的应用——编辑旧文章) ...

  9. I2C和SCCB协议的小区别和误区

    I2C采用的是7位地址数据,首字节最低位代表读写位,第二字节SUB寄存器高位为1表示自动加subaddress SCCB采用的是8位地址数据,比如0x42,B01000010 使用I2C协议时,第一次 ...

  10. asp.net mvc 两级分类联动方法示例

    前台视图代码 <%:Html.DropDownList("AwardClassMainID","请选择")%> <%:Html.DropDow ...