[学习笔记]设计模式之Facade
写在前面
为方便读者,本文已添加至索引:
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 }
还有调整后的WeaponFactory类以及它的子类们:
1 class WeaponFactory { 2 public: 3 virtual Weapon* createWeapon() = 0; 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 ...
以及前文中提到的附魔工坊:
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 }
暂时还没有建成的护甲工坊:
1 class ArmorFactory : public VisualObject { 2 public: 3 virtual Armor* createArmor() = 0; 4 }
好啦,该去参观参观格琳达的小屋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 == 0) { 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 = 0; 44 Weapon* weapon = 0; 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模式简单且易于理解,对于我们来说也很常用。它的优点如下:
- 它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
- 它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。Facade模式可以消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要。
- 如果应用需要,它并不限制它们使用子系统类。因此我们可以在系统易用性和通用性之间加以选择。
写在最后
今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!