系列文章目录
C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
设计模式系列
期待你的关注哦!!!
现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.
一、外观模式介绍
⚠️ 意图:
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
⚠️ 主要解决:
降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。
⚠️ 何时使用:
1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。 2、定义系统的入口
⚠️ 如何解决:
客户端不与系统耦合,外观类与系统耦合。
(1)外观模式符合是“迪米特法则”,通过引入一个新的外观类可以降低原有系统的复杂度,外观类充当了客户类与子系统类之间的“第三者”,同时降低客户类与子系统类的耦合度。外观模式就是实现代码重构以便达到“迪米特法则”要求的一个强有力的武器。
(2)外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。
(3)外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。
(4)不要试图通过外观类为子系统增加新行为 ,不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现。
(5)在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然并不意味着在整个系统里只能有一个外观类,在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供相应的业务功能。
(6)当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。
二、外观模式优缺点
2.1 优点
-
对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少。
-
实现了子系统与客户之间的松耦合关系,使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
-
降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
-
只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。
2.2 缺点
-
不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
-
在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
三、外观模式使用场景
-
当要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类,使得子系统更具可重用性,也更容易对子系统进行定制,但也给那些不需要定制子系统的用户带来一些使用上的困难。facade可以提供一个简单的缺省视图,视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
-
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入 facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性 和可移植性。
-
当你需要构建一个层次结构的子系统时,使用 facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
适配器模式和外观模式共同点是将不好用的几口适配成好用的接口。但是适配器模式是将一个接口通过适配来间接转换为另一个接口,外观模式主要是提供一个整洁的一致的接口给客户端。
抽象工厂模式可以与外观模式一起使用以提供一个接口,用来以一种子系统独立的方式创建子系统对象。
中介者模式与外观模式抽象了一些已有的类的功能。中介者模式的目的是对同事之间的任意通讯进行抽象,通常集中不属于任何单个对象的功能。中介者模式的同事对象知道中介者并与中介者通信,而不是直接与其他同类对象通信。外观模式仅对子系统对象的接口进行抽象,从而使子系统的接口更容易使用;外观模式并不定义新功能,子系统也不知道Facade的存在。
一般仅需要一个Facade对象,因此Facade对象通常属于Singleton模式。
四、外观模式实现
Facade外观类:
#ifndef FACADE_H
#define FACADE_H
#include <iostream>
#include "SubSystemA.h"
#include "SubSystemB.h"
class Facade
{
public:
Facade()
{
m_pSubSystemA = new SubSystemA();
m_pSubSystemB = new SubSystemB();
}
~Facade()
{
delete m_pSubSystemA;
delete m_pSubSystemB;
m_pSubSystemA = NULL;
m_pSubSystemB = NULL;
}
void operationWrapper()
{
m_pSubSystemA->operation();
m_pSubSystemB->operation();
}
private:
SubSystemA* m_pSubSystemA;
SubSystemB* m_pSubSystemB;
};
#endif // FACADE_H
SubSystemA子系统类:
#ifndef SUBSYSTEMA_H
#define SUBSYSTEMA_H
#include <iostream>
class SubSystemA
{
public:
SubSystemA(){}
~SubSystemA(){}
void operation()
{
std::cout << "SubSystemA::operation" << std::endl;
}
};
#endif // SUBSYSTEMA_H
SubSystemB子系统类:
#ifndef SUBSYSTEMB_H
#define SUBSYSTEMB_H
#include <iostream>
class SubSystemB
{
public:
SubSystemB(){}
~SubSystemB(){}
void operation()
{
std::cout << "SubSystemB::operation" << std::endl;
}
};
#endif // SUBSYSTEMB_H
客户调用程序:
#include "Facade.h"
int main()
{
Facade* facade = new Facade();
facade->operationWrapper();
return 0;
}
五、外观模式应用实例
假设编译一个程序需要经过四个步骤:词法分析、语法分析、中间代码生成、机器码生成。对于编译器系统,可以使用外观模式,定义一个高层接口Compiler类,Compiler有一个名为compile的函数。客户只需调用compile函数就可以编译程序,compile函数内部的具体操作,客户无需知道。
#include <iostream>
using namespace std;
//subsystem
class Scanner
{
public:
void scan()
{
cout << "scanning..." << endl;
}
};
class Parser
{
public:
void parse()
{
cout << "parsing..." << endl;
}
};
class GenMidCoder
{
public:
void generateCode()
{
cout << "generating middle code...." << endl;
}
};
class GenMachineCoder
{
public:
void generateCode()
{
cout << "generating machine code...." << endl;
}
};
//Facade
class Compiler
{
public:
void compile()
{
Scanner sanner;
Parser parser;
GenMidCoder genMidCoder;
GenMachineCoder genMachineCoder;
sanner.scan();
parser.parse();
genMidCoder.generateCode();
genMachineCoder.generateCode();
}
};
int main()
{
Compiler compiler;
compiler.compile();
return 0;
}