使用设计模式可以提高代码的可复用性、可扩充性和可维护性。外观模式(Facade Pattern)属于结构型模式,提供了一个统一的接口(具体类),用来访问子系统的一群接口(具体类)。外观定义了一个高层接口,让子系统更容易使用。
要想使用外观模式,我们需要创建一个函数接口简化而统一的类,用来包装子系统中一个或多个复杂的类。外观模式类结构清晰,容易理解,允许我们让客户和子系统之间避免紧耦合。
类图结构如下:
Client:客户类,通过外观类与子系统进行交互。 Facade:外观类,知道哪些子系统类负责处理请求,将客户端的请求代理给适当的子系统对象。 Subsystem:子系统类,实现子系统功能,处理外观类指派的任务,注意子系统类不含有外观类的引用。
外观模式应用了最少知道原则(Least Knowledge Principle),又称为迪米特法则(Law of Demeter)。该原则要求一个对象减少对其他对象的交互,只与几个“密友”交谈。
最少知道原则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类之间建立直接的联系,如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系,这在一定程度上增加了系统的复杂度。
最少知道原则在中介者模式中也有应用。
本文我们举武侠的例子,我们把《倚天屠龙记》张无忌当作一个系统,他作为一个武侠,本身分为三个子系统,分别是招式、内功和经脉。
张无忌的三个子系统分别是招式、内功和经脉,创建如下:
//子系统招式
class ZhaoShi {
public:
void taiJiQuan() {
cout << "使用着招式太极拳" << endl;
}
void qiShangQuan() {
cout << "使用招式七伤拳"<<endl;
}
void shengHuoLing() {
cout << "使用招式圣火令"<<endl;
}
};
//子系统内功
class NeiGong {
public:
void jiuYang(){
cout << "使用九阳神功"<<endl;
}
void qianKun() {
cout << "使用乾坤大挪移"<<endl;
}
};
//子系统经脉
class JingMai {
public:
void jingMai() {
cout << "开启经脉"<<endl;
}
};
张无忌有很多的招式和内功,怎么将它们搭配,并对外界隐藏呢?这里的外观类就是张无忌,他负责将自己的招式、内功和经脉通过不同的情况合理的运用。
//外观类张无忌
class ZhangWuJi {
private:
JingMai jingMai;
ZhaoShi zhaoShi;
NeiGong neiGong;
public:
ZhangWuJi(){}
//使用乾坤大挪移
void qianKun() {
jingMai.jingMai();//开启经脉
neiGong.qianKun();//使用内功乾坤大挪移
}
//使用七伤拳
void qiShang() {
jingMai.jingMai(); //开启经脉
neiGong.jiuYang(); //使用内功九阳神功
zhaoShi.qiShangQuan();//使用招式七伤拳
}
};
初始化外观类的同时将各个子系统类对象创建好。很明显张无忌将各个系统搭配好,如使用七伤拳的话就需要开启经脉和内功九阳神功,使七伤拳的威力发挥到极致。
客户端调用:
int main() {
ZhangWuJi zhangWuJi;
//张无忌使用乾坤大挪移
zhangWuJi.qianKun();
//张无忌使用七伤拳
zhangWuJi.qiShang();
}
程序输出结果:
开启经脉
使用乾坤大挪移
开启经脉
使用九阳神功
使用招式七伤拳
当张无忌使用乾坤大挪移或者七伤拳的时候,比武的对手显然不知道张无忌本身运用了什么,同时张无忌也不需要去重新计划使用七伤拳的时候需要怎么做,他已经早就计划好了。如果每次使用七伤拳或者乾坤大挪移时都要计划怎么做很显然会增加成本并贻误战机。另外张无忌也可以改变自己的经脉、内功和招式,这些对比武对手都是隐藏的。
外观模式本身将客户端与子系统的交互封装起来,为用户提供一个高层次的类接口,降低了客户端与子系统的耦合度,当子系统发生改变时,不会影响到客户端代码,使得系统易于使用和维护。同时也隐藏了子系统的具体实现,即使具体的子系统发生了变化,客户端也不会感知到。
应用场景: (1)客户端需要与多个子系统交互时,可使用外观模式。如当维护一个遗留的大型系统时,可能这个系统难以维护和拓展,但因为含有重要功能,新的需求必须依赖于它,则可以使用外观类,来为这个粗糙复杂的遗留代码提供一个简单的类接口,让新系统和外观类交互,而外观类负责与遗留的代码进行交互。 (2)客户端程序与多个子系统之间存在很大的依赖性。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性。
优点: (1)松散耦合。构建一个有层次结构的子系统时,使用外观模式定义子系统中每层的入口点,如果子系统之间是相互依赖的,则可以让他们通过外观接口进行通信,减少子系统之间的依赖关系。
(2)易于使用。子系统往往会因为不断的重构演化而变得越来越复杂,大多数模式使用时也会产生很多很小的类,这给外部调用他们的用户程序带来了使用的困难,我们可以使用外观类提供一个简单的接口,对外隐藏子系统的具体实现并隔离变化。
(3)编译依赖性。降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。
缺点: 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开放关闭原则”
(1)外观模式属于结构型模式,提供了一个统一的类接口,用来访问子系统的一群类接口。外观定义了一个高层类接口,让子系统更容易使用。 (2)最少知道原则要求一个对象尽量减少对其他对象的交互,只与少数的类对象进行交互。
[1]最少知道原则.百度百科 [2]设计模式读书笔记—–外观模式 [3]设计模式(八)外观模式