模式概述
外观模式是设计模式中比较常见的一种,该模式提供一个外观类,负责对外打交道的门户,它屏蔽了子系统内部特定的调用关系,简化接口的使用;该模式的定义如下:
外观模式(Facade)为子系统中的一组接口提供了一个一致的界面,此模式定义了一个高层接口,这些接口使得这一子系统更加容易使用. 其基本的结构体如下:
在这个结构中包含两个角 {MOD},一个是外观类Facade,一个是子系统类;其作用如下:
Facade类(外观类):对外提供一个高层接口,将客户的请求交给适当子系统进行处理;
SubSystem Classees: 是子系统类的集合,每个子系统都完成一个特定的功能,这些子系统可以是某个模块,也可是某些类,Facade类将用户的请求交给这些子系统进行处理;需要注意的是子类中没有Facade类的任何信息,即没有对Facade类对象的引用;
将以上结构图转换为代码如下:
class SubSystemOne
{
public:
void MethodOne()
{
cout << "call subsystem method one" << endl;
}
};
class SubSystemTwo
{
public:
void MethodTwo()
{
cout << "call subsystem method two" << endl;
}
};
class SubSystemThree
{
public:
void MethodThree()
{
cout << "call subsystem method three" << endl;
}
};
class SubSystemFour
{
public:
void MethodFour()
{
cout << "call subsystem method four" << endl;
}
};
class CFacade
{
public:
void MethondA()
{
cout <<"-----Call MethondA-----" << endl;
SubSystemOne systemOne;
systemOne.MethodOne();
}
void MethondB()
{
cout <<"-----Call MethondB-----" << endl;
SubSystemOne systemOne;
SubSystemTwo systemTwo;
SubSystemThree systemThree;
systemOne.MethodOne();
systemTwo.MethodTwo();
systemThree.MethodThree();
}
void MethondC()
{
cout <<"-----Call MethondC-----" << endl;
SubSystemThree systemThree;
SubSystemFour systemFour;
systemThree.MethodThree();
systemFour.MethodFour();
}
};
客户端代码:
CFacade Facede
Facede.MethondA()
Facede.MethondB()
Facede.MethondC()
由于Facade类对象的作用,客户端可以根本不知道四个子系统的存在,体现了面向对象的最少知识原则;
运行结果
外观模式应用
进行Web编程中,都会采用经典的MVC三层架构,即Web表示层,业务逻辑层,数据访问层;在层和层之间通常会考虑增加Facade类,以简化接口调用,其三层架构示意图如下:
比如我们在开发企业员工系统时,我们会设计员工表和薪资表的,其内容如下面CBaseInfo类和CSalary类;每个类都有对应的数据访问层接口,即CBaseInfoDao类和CSalaryDao类。
为了简化业务逻辑层的接口调用,我们增加一个CEmployeeDao接口类,这个类就充当数据访问层CBaseInfoDao和CSalaryDao子系统的外观Facade。
示意代码如下:
typedef enum
{
FEMALE = 0,
MALE = 1,
}ENUM_SEX_TYPE;
class CBaseInfo
{
public:
CBaseInfo():
m_strName(""),m_usAge(0),m_enSex(FEMALE),m_usID(0){}
~CBaseInfo(){}
private:
string m_strName;
unsigned m_usAge;
ENUM_SEX_TYPE m_enSex;
unsigned m_usID;
};
class CSalary
{
public:
CSalary():m_baseSalary(0),
m_bonus(0),m_FullAttendencAward(0),m_usID(0){}
~CSalary(){}
private:
float m_baseSalary;
float m_bonus;
float m_FullAttendencAward;
int m_usID;
};
class CEmployeeInfo
{
public:
CEmployeeInfo(){}
~CEmployeeInfo(){}
public:
CBaseInfo m_BaseInfo;
CSalary m_Salary;
};
class CBaseInfoDao
{
public:
void InserBaseInfo(const CBaseInfo& info)
{
cout << "insert base info data..." << endl;
}
CBaseInfo GetBaseInfo()
{
cout << "get base info data..." << endl;
CBaseInfo info;
return info;
}
void UpdateBaseInfo(const CBaseInfo& info)
{
cout << "update salary data..." << endl;
}
};
class CSalaryDao
{
public:
void InserSalary(const CSalary& salary)
{
cout << "insert salary data..." << endl;
}
CSalary GetSalary()
{
cout << "get salary data..." << endl;
CSalary salary;
return salary;
}
void UpdateSalary(const CSalary& salary)
{
cout << "update salary data..." << endl;
}
};
class CEmployeeDao
{
public:
void InsertEmployeeInfo(const CEmployeeInfo& employee)
{
cout << "-----inset employ info-----" << endl;
m_BaseInfoDao.InserBaseInfo(employee.m_BaseInfo);
m_SalaryDao.InserSalary(employee.m_Salary);
}
void UpdateEmployeeInfo(const CEmployeeInfo& employee)
{
cout << "-----update employ info-----" << endl;
m_BaseInfoDao.UpdateBaseInfo(employee.m_BaseInfo);
m_SalaryDao.UpdateSalary(employee.m_Salary);
}
private:
CBaseInfoDao m_BaseInfoDao;
CSalaryDao m_SalaryDao;
};
客户端代码
CEmployeeDao employeeDao;
CEmployeeInfo employeeInfo;
employeeDao.InsertEmployeeInfo(employeeInfo);
employeeDao.UpdateEmployeeInfo(employeeInfo);
运行结果:
模式总结
外观模式是一种使用频率非常高的设计模式,它通过引入一个外观角 {MOD}来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,使子系统与客户端的耦合度降低,且客户端调用非常方便。外观模式并不给系统增加任何新功能,它仅仅是简化调用接口。
在几乎所有的软件中都能够找到外观模式的应用,如绝大多数B/S系统都有一个首页或者导航页面,大部分C/S系统都提供了菜单或者工具栏,在这里,首页和导航页面就是B/S系统的外观角 {MOD},而菜单和工具栏就是C/S系统的外观角 {MOD},通过它们用户可以快速访问子系统,降低了系统的复杂程度。所有涉及到与多个业务对象交互的场景都可以考虑使用外观模式进行重构。
1 模式优点
外观模式的主要优点如下:
(1) 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。
(2) 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
(3) 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
2 模式缺点
外观模式的主要缺点如下:
如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则, 增加抽象外观类,从一定程度上可以减少代码修改;
3 适用场景
- 在设计初期阶段,应该有意识的将不同的两个层分离或者系统模块化,比如经典的三层架构,就需要在考虑数据访问层和业务逻辑层、业务逻辑层和表示层的层和层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合性大大降低.
- 在开发阶,由于子系统的不断重构演变而变的越来越复杂,客户端程序调用子系统变得困难,此时可以引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。
- 在维护一个遗留的大型系统时,可以能这个系统已经非常难以维护和扩展了,但因为包含非常重要的功能,新的需求必须依赖它。此时使用外观模式也是非常适合的。我们为新系统开发一个Facade外观类,让新系统与Facade对象交互,Facade对象与遗留代码交互完成所有复杂的工作。