其实这个模式在日常使用过程中被使用到的频率特别高,只是不清楚原来这种这么简单的手法还有一个挺高大上的名字 —— 外观模式
,又叫 门面模式
意图
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
动机
我很喜欢 《大话设计模式》 引出这种设计模式时用到的例子,基金和股票的关系
众所周知,股票 是一种相对而言风险比较高的理财方式,一支股票由一支公司发行的,当我们持有多家的优质公司发行的股票的时候,因为涉及到对自己投资资金的规划,随着市场的波动,我们可能需要动态的调整持仓的占比等等,因此我们的操作的复杂度也上来了,可这对于大多数理财小白而言上手难度有点大
而什么是 基金 呢?简单点说就是安排个基金经理,他用资金去投资他认为优秀的股票,债券等等领域,由他来全权负责。这意味如果我们买了基金,实际上我们是不用操作股票,只有一个简单的买卖接口
所以对于我而言,直接选择一支优秀的基金,然后交给基金经理在后台对持仓进行复杂的调整
关于理财感兴趣的可以看一下 B 站 UP 主 这是Morty
的科普视频 https://www.bilibili.com/video/BV16z4y167tz
扯远了,回来 外观模式
,这里的基金其实就是股票的一个 门面
,我们可以很简单的操作基金提供的 高层接口
, 不用操作复杂的子系统股票,当然如果需要定制化操作时,用户也可以越过基金 (门面
) 这一层
结构图
外观模式其实没有所谓的 UML
图, 更多是对复杂子系统调用的一种结构调整
图中,客户端本来是操作 对象A
和 对象C
的,使用外观模式将整个子系统包装一下,让客户端通过 Facade
来操作,此时的 Facade
是可以提供一组更为简洁的接口
- 它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
- 降低了客户-子系统之间的耦合度
用抽象类实现Facade
而它的具体子类对应于不同的子系统的实现,这样可以进一步降低客户和子系统的耦合度
使用场景
- 当你要为一个复杂的子系统提供一个简单的接口时。
子系统往往因为不断的演化而变得越来越复杂,大多数模式使用时都会产生更多更小的类,这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。外观模式可以提供一个简单的缺省实现,而这个对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade
层 - 客户程序与抽象类的实现部分之间存在着很大的依赖性,引入
Facade
将这个子系统与客户端以及其他的子系统分离,可以提高子系统的独立性和可移植性 - 当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让他们仅通过
Facade
进行通信,从而简化了他们之间的依赖关系
来个例子
直接前面提到的股票和基金的例子:
先定义2支股票,分别是茅台和特斯拉:
#include <iostream>
using namespace std;
class MoutaiStock
{
public:
void buy(int money) {cout << "buy MoutaiStock: " << money << endl;}
};
class TeslaStock
{
public:
void buy(int money) {cout << "buy TeslaStock: "<< money << endl;}
};
定一支基金,主要就是持有这2支股票,在买入基金的时候,7成买茅台股票,3成买特斯拉股票:
#include "shares.h"
#define SAFE_DELETE(p) if(p) {delete p; p=nullptr;}
class EFund{
public:
EFund() : m_pMoutaiStock(new MoutaiStock), m_pTeslaStock(new TeslaStock) {}
~EFund() { SAFE_DELETE(m_pMoutaiStock); SAFE_DELETE(m_pTeslaStock); }
void buy(int money){
cout << "buy EFund: " << money << endl;
m_pMoutaiStock->buy(money/10*7);
m_pTeslaStock->buy(money/10*3);
}
private:
MoutaiStock *m_pMoutaiStock;
TeslaStock *m_pTeslaStock;
};
最后客户端买基金的代码就很简单了
#include "fund.h"
int main()
{
EFund e_fund;
e_fund.buy(100);
return 0;
}
运行结果
buy EFund: 100
buy MoutaiStock: 70
buy TeslaStock: 30
总结
外观模式在我眼里更多是个重新封装的过程,将一些复杂模块的代码进行封装从而对客户隐藏具体细节,并且提供给客户更简洁的接口~