写在前面

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

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

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

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

要点梳理

  • 目的分类

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

示例分析 - 格琳达的小屋

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

首先是我们基本的Weapon类:

  1. class Weapon : public VisualObject {
  2. public:
  3. Weapon(int);
  4. ~Weapon() { delete _impl; }
  5. // ... other ...
  6. protected:
  7. WeaponImpl* getWeaponImpl();
  8. private:
  9. WeaponImpl* _impl;
  10. }
  11.  
  12. Weapon::Weapon(int type) {
  13. switch(type)
  14. {
  15. case WEAPON_SWORD:
  16. _impl = new SwordImpl();
  17. break;
  18. case WEAPON_SHIELD:
  19. _impl = new ShieldImpl();
  20. break;
  21. case WEAPON_BOW:
  22. _impl = new BowImpl();
  23. break;
  24. // ... other cases ...
  25. default:
  26. break;
  27. }
  28. }

Weapon

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

  1. class WeaponFactory {
  2. public:
  3. virtual Weapon* createWeapon() = ;
  4. }
  5.  
  6. class HonorOfFighter : public WeaponFactory {
  7. public:
  8. HonorOfFighter();
  9. Weapon* createWeapon() { return new Weapon(WEAPON_SWORD); }
  10. }
  11.  
  12. class BliefOfDefender : public WeaponFactory {
  13. public:
  14. BliefOfDefender();
  15. Weapon* createWeapon() { return new Weapon(WEAPON_SHEILD); }
  16. }
  17.  
  18. class PrecisionOfHunter : public WeaponFactory {
  19. public:
  20. PrecisionOfHunter();
  21. Weapon* createWeapon() { return new Weapon(WEAPON_BOW); }
  22. }
  23.  
  24. // ... other weapon factories ...

WeaponFactory

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

  1. #define ENCHANTED_ICE 1
  2. #define ENCHANTED_FIRE 2
  3. #define ENCHANTED_SHOCK 3
  4.  
  5. class EnchantedWeaponFactory {
  6. public:
  7. Weapon* createEnchantedWeapon(int, int);
  8. // ... other ...
  9. }
  10.  
  11. EnchantedWeaponFactory::createEnchantedWeapon(int element, int type) {
  12. return new EnchantedWeapon(element, type);
  13. }

EnchantedWeaponFactory

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

  1. class ArmorFactory : public VisualObject {
  2. public:
  3. virtual Armor* createArmor() = ;
  4. }

ArmorFactory

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

  1. #define NOTHING 0
  2.  
  3. class HouseOfGlinda {
  4. public:
  5. VisualObject* onSale(int, int); // a simple interface.
  6.  
  7. // ... It's a Singleton ...
  8. static HouseOfGlinda* getInstance() {
  9. if (_instance == ) {
  10. _instance = new HouseOfGlinda();
  11. }
  12.  
  13. return _instance;
  14. }
  15.  
  16. protected:
  17. Weapon* getWeapon(int);
  18. Weapon* getEnchantedWeapon(int, int);
  19.   // ... Armor will be comming soon ...
  20. private:
  21. HouseOfGlinda();
  22. static HouseOfGlinda* _instance;
  23. }
  24.  
  25. VisualObject* HouseOfGlinda::onSale(int type, int info) {
  26. if (WEAPON_SWORD == type ||
  27. WEAPON_SHIELD == type ||
  28. WEAPON_BOW == type
  29. // ... other weapon ...
  30.   ) {
  31.  
  32. if (NOTHING == info) {
  33. return getWeapon(type);
  34. }
  35. else {
  36. return getEnchantedWeapon(type, info);
  37. }
  38. }
  39. // else for armor ...
  40. }
  41.  
  42. Weapon* HouseOfGlinda::getWeapon(int type) {
  43. WeaponFacotry* factory = ;
  44. Weapon* weapon = ;
  45. switch(type) {
  46. case WEAPON_SWORD:
  47. factory = new HonorOfFighter();
  48. break;
  49. case WEAPON_SHIELD:
  50. factory = new BliefOfDefender();
  51. break;
  52. // ... other cases ...
  53. default:
  54. break;
  55. }
  56. weapon = factory->createWeapon();
  57. if (factory) delete factory;
  58. return weapon;
  59. }
  60.  
  61. Weapon* HouseOfGlinda::getEnchantedWeapon(int type, int info) {
  62. EnchantedWeaponFactory factory;
  63. return factory.createEnchantedWeapon(info, type);
  64. }

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

  1. VisualObject* obj1 = HouseOfGlinda::getInstance->onSale(WEAPON_SHIELD, ENCHANTED_FIRE);
  2. 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. bzoj 1069 [SCOI2007]最大土地面积(旋转卡壳)

    1069: [SCOI2007]最大土地面积 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2277  Solved: 853[Submit][Stat ...

  2. POJ 3186Treats for the Cows (区间DP)

    详见代码 #include <stdio.h> #include <algorithm> #include <string.h> using namespace s ...

  3. Bzoj 2453: 维护队列 && Bzoj 2120: 数颜色 分块,bitset

    2453: 维护队列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 578  Solved: 247[Submit][Status][Discuss] ...

  4. LCD framebuffer驱动设计文档

    内容提要:1. android display相关的名词2. 调试LCD驱动需要注意的步骤3. 关于帧缓冲区及I/O内存---------------------------------------- ...

  5. 一步一步写一个简单通用的makefile(三)

    上一篇一步一步写一个简单通用的makefile(二) 里面的makefile 实现对通用的代码进行编译,这一章我将会对上一次的makefile 进行进一步的优化. 优化后的makefile: #Hel ...

  6. php非阻塞执行系统命令

    大家都知道php调用系统命令常用的主要有以下几种方法: 如exec(), system(), passthru(), shell_exec() 这几个函数的用法在此不做说明,有需要的请查阅php相关手 ...

  7. OpenSUSE SuSEfirewall2

    1,修改SuSEfirewall2配置文件放行相应的端口方法vim /etc/sysconfig/SuSEfirewall2#TCP端口的情况:FW_SERVICES_EXT_TCP ="2 ...

  8. pom.xml第一行报错

    错误:Cannot detect Web Project version. Please specify version of Web Project through <version> ...

  9. Oracle学习.Windows 命令行 启动ORACLE服务与实例

    使用数据库前要先打开数据库的实例和监听器! --总结启动命令如下: lsnrctl  [start|stop|status]                  --启动监听器,停止监听器,查看监听器的 ...

  10. .NET DataTable转化为json格式

    标准的json用“分隔,不用' public static string DataSetToJson(DataTable dt) {        string json = string.Empty ...