在软件开发中,有时候为了完成一项较为复杂的功能,一个类需要和多个其它类进行交互,而这些需要交互的其它类通常作为一个完整的整体出现,由于涉及的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它复杂和多个业务类交互,使用这些业务类的类只需要和该类交互即可。外观模式,通过引入一个新的外观类来实现该功能,外观类充当了软件系统的“服务员”,它为多个业务类的调用提供了统一的入口,简化了类与类之间的交互。
外观模式概述
外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观角色进行,外观角色将客户端和子系统的内部复杂性分开,使得客户端只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道。
外观模式示意图如下:
外观模式定义
外部与一个子系统的通信通过一个统一的外观角色进行,外观角色为子系统的一组接口提供一个一致的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式的结构描述形式如下图:
在外观角色结构图中包含以下两个角色:
(1) Facade(外观角色) : 在客户端可以调用这个角色的方法,在外观角色中可以知道相关的一个或者多个子系统的功能和职责。在正常情况下,外观角色将从客户端发来的请求委派到相应的子系统中去,传递给相应的子系统对象处理。
(2) SubSystem(子系统角色): 在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统可以被客户端直接调用或者被外观角色调用,它处理外观类传递来的请求;子系统并不知道外观的存在,对子系统而言,外观角色仅仅是另外一个客户端而已。
外观模式的引入很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。在外观角色中维持了对子系统对象的引用,客户端通过外观角色间接调用子系统对象的业务方法,而无须与子系统对象直接狡猾。
外观角色设计补充说明
(1) 外观类可以是一个单例类,以降低对系统资源的消耗。
(2) 在一个系统中可以设计多个外观类。
(3) 外观类不应该为子系统增加新行为。
外观模式总结
在几乎所有的软件中都能找到外观模式的应用,例如,绝大多数B/S系统都有一个首页或者导航页面,大部分C/S系统都有菜单栏和工具栏,在这里,首页和导航页面就是B/S系统的外观角色,菜单栏和工具栏就是C/S系统的外观角色,通过它们可以快速访问子系统,降低系统的复杂程度。
主要优点
(1) 客户端代码将变得很简答,与之关联的对象也很少。
(2) 实现客户端和子系统之间的松耦合。
主要缺点
(1) 不能很好地限制客户端直接使用子系统类。
(2) 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背开闭原则。
适用场景
(1) 当要为访问一系列复杂子系统提供一个简单入口。
(2) 将子系统与客户端解耦。
(3) 在层次化的结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而是通过外观类 建立联系,降低层之间的耦合度。
外观模式应用实例
文件加密模块结构图如下:
示例代码如下(C++):
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
//文件读取类:子系统类
class FileReader{
public:
vector<string> read(string fileNameSrc){
cout<<"读取文件,获得明文:"<<endl;
vector<string> plainStr;
ifstream input(fileNameSrc.c_str());
string line;
while(getline(input,line)){
plainStr.push_back(line);
cout<<line<<endl;
}
return plainStr;
}
};
//数据加密类 :子系统类
class CipherMachine{
public:
vector<string> encrypt(vector<string> plainStr){
cout<<"数据加密,将明文转换为密文:"<<endl;
vector<string> encryptStr;
vector<string>::iterator iter=plainStr.begin();
while(iter!=plainStr.end()){
string temp=*iter;
unsigned int i=0;
while(i<temp.size()){
temp[i]=temp[i]%7+'0';
i++;
}
cout<<temp<<endl;
encryptStr.push_back(temp);
++iter;
}
return encryptStr;
}
};
//文件保存类: 子系统类
class FileWriter{
public:
void write(string fileNameDest,vector<string> encryptStr){
cout<<"保存密文,写入文件"<<endl;
ofstream output(fileNameDest.c_str());
vector<string>::const_iterator iter=encryptStr.begin();
while(iter!=encryptStr.end()){
output<<*iter<<endl;
++iter;
}
}
};
//加密外观类: 外观类
//此处设计成单例外观类
class EncryptFacade{
private:
//维持对其它对象的引用
FileReader *reader;
CipherMachine *cipher;
FileWriter *writer;
EncryptFacade(){
reader=new FileReader();
cipher=new CipherMachine();
writer=new FileWriter();
}
static EncryptFacade* instance; //饿汉单例模式,保证安全的多线程访问
public:
//获得单例类的唯一实例
static EncryptFacade* getInstance(){
return instance;
}
//统一的接口,调用其它对象的业务方法
void fileEncrypt(string fileNameSrc,string fileNameDest){
vector<string> plainStr=reader->read(fileNameSrc);
vector<string> encryptStr=cipher->encrypt(plainStr);
writer->write(fileNameDest,encryptStr);
}
};
//静态成员变量,类内定义,类外初始化
EncryptFacade* EncryptFacade::instance=new EncryptFacade();
//客户端测试代码
int main(){
EncryptFacade *encryFacade=EncryptFacade::getInstance();
string fileNameSrc("plainText.txt");
string fileNameDest("encryptText.txt");
encryFacade->fileEncrypt(fileNameSrc,fileNameDest);
return 0;
}
运行结果截图:
抽象外观类的引入
在标准的外观模式结构图中,如果需要增加、删除或者更换与外观类交互的子系统类,必须修改客户端或者外观类的源代码,这将违背开闭原则,因此可以引入抽象外观类对系统进行改进,在一定程度上解决该问题。在引入抽象外观类之后,客户端可以针对抽象外观类进行编程,对于新的业务需求,不需要修改原有外观类,而对于增加一个新的具体外观类,由新的具体外观类关联新的子系统对象,同时通过修改配置文件达到不修改任何源代码并更换外观类的目的。
下面通过一个实例来介绍如何使用抽象外观类:
如果在sunny软件公司开发的文件加密模块中需要更换一个加密类,不再使用原有的基于求模运算的加密类ChiperMachine,而改成基于移位运算的新加密类NewChiperMachine。
为了使系统符合开闭原则,解决方法是引入一个抽象外观类,客户端针对抽象外观类进行编程,而在运行时再确定具体外观类。
引入抽象外观类后文件加密模块结构图如下:
示例代码如下:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
//文件读取类:子系统类
class FileReader{
public:
vector<string> read(string fileNameSrc){
cout<<"读取文件,获得明文:"<<endl;
vector<string> plainStr;
ifstream input(fileNameSrc.c_str());
string line;
while(getline(input,line)){
plainStr.push_back(line);
cout<<line<<endl;
}
return plainStr;
}
};
//求模运算数据加密类 :子系统类
class CipherMachine{
public:
vector<string> encrypt(vector<string> plainStr){
cout<<"数据加密,将明文转换为密文:"<<endl;
vector<string> encryptStr;
vector<string>::iterator iter=plainStr.begin();
while(iter!=plainStr.end()){
string temp=*iter;
unsigned int i=0;
while(i<temp.size()){
temp[i]=temp[i]%7+'0';
i++;
}
cout<<temp<<endl;
encryptStr.push_back(temp);
++iter;
}
return encryptStr;
}
};
//移位运算数据加密类 :子系统类
class NewChiperMachine{
public:
vector<string> encrypt(vector<string> plainStr){
cout<<"数据加密,将明文转换为密文:"<<endl;
vector<string> encryptStr;
vector<string>::iterator iter=plainStr.begin();
while(iter!=plainStr.end()){
string temp=*iter;
int key=10;//设置密钥,移位次数为10
unsigned int i=0;
while(i<temp.size()){
//对小写字母移位
if(temp[i]>'a' && temp[i]<'z'){
unsigned char c=temp[i];//处理汉字的情况,汉字字节最高位为1
c+=key%26;
if(c>'z')
c-=26;
temp[i]=c;
}
//对大写字母移位
if(temp[i]>'A' && temp[i]<'Z'){
unsigned char c=temp[i];
c+=key%26;
if(c>'Z')
c-=26;
temp[i]=c;
}
i++;
}
cout<<temp<<endl;
encryptStr.push_back(temp);
++iter;
}
return encryptStr;
}
};
//文件保存类: 子系统类
class FileWriter{
public:
void write(string fileNameDest,vector<string> encryptStr){
cout<<"保存密文,写入文件"<<endl;
ofstream output(fileNameDest.c_str());
vector<string>::const_iterator iter=encryptStr.begin();
while(iter!=encryptStr.end()){
output<<*iter<<endl;
++iter;
}
}
};
//抽象外观类
class AbstractEncryptFacade{
public:
virtual void fileEncrypt(string,string)=0;
};
//加密外观类: 外观类
//此处设计成单例外观类
class EncryptFacade : public AbstractEncryptFacade{
private:
//维持对其它对象的引用
FileReader *reader;
CipherMachine *cipher;
FileWriter *writer;
EncryptFacade(){
reader=new FileReader();
cipher=new CipherMachine();
writer=new FileWriter();
}
static EncryptFacade* instance; //饿汉单例模式,保证安全的多线程访问
public:
//获得单例类的唯一实例
static EncryptFacade* getInstance(){
return instance;
}
//统一的接口,调用其它对象的业务方法
void fileEncrypt(string fileNameSrc,string fileNameDest){
vector<string> plainStr=reader->read(fileNameSrc);
vector<string> encryptStr=cipher->encrypt(plainStr);
writer->write(fileNameDest,encryptStr);
}
};
//静态成员变量,类内定义,类外初始化
EncryptFacade* EncryptFacade::instance=new EncryptFacade();
//新加密外观类: 外观类
//此处设计成单例外观类
class NewEncryptFacade : public AbstractEncryptFacade{
private:
//维持对其它对象的引用
FileReader *reader;
NewChiperMachine *cipher;
FileWriter *writer;
NewEncryptFacade(){
reader=new FileReader();
cipher=new NewChiperMachine();
writer=new FileWriter();
}
static NewEncryptFacade* instance; //饿汉单例模式,保证安全的多线程访问
public:
//获得单例类的唯一实例
static NewEncryptFacade* getInstance(){
return instance;
}
//统一的接口,调用其它对象的业务方法
void fileEncrypt(string fileNameSrc,string fileNameDest){
vector<string> plainStr=reader->read(fileNameSrc);
vector<string> encryptStr=cipher->encrypt(plainStr);
writer->write(fileNameDest,encryptStr);
}
};
//静态成员变量,类内定义,类外初始化
NewEncryptFacade* NewEncryptFacade::instance=new NewEncryptFacade();
//客户端测试代码
int main(){
//针对抽象外观类编程,使用新的文件加密外观类
AbstractEncryptFacade *newEncryFacade=NewEncryptFacade::getInstance();
string fileNameSrc("plainText.txt");
string fileNameDest("encryptText.txt");
newEncryFacade->fileEncrypt(fileNameSrc,fileNameDest);
return 0;
}
运行结果截图如下: