1. 外观(门面)模式(Facade Pattern)的定义
(1)为子系统中的一组接口提供一个一致的界面,Façade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
①这里说的界面是指从一个组件外部来看这个组件,能够看到什么,也就是外观。如从一个类外部看这个类,那么这个类的public方法或接口就是他的外观。
②这里所说的“接口”是指外部和内部交互的一个通道(方法),可以是类的方法,也可以是interface的方法。
(2)外观模式的结构和说明
①Façade:定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,从而把客户的请求代理给适当的子系统对象
②模块:接受Façade对象的委派,真正实现功能,各个模块之间可能有交互。但请注意,Façade对象知道各个模块,但是各个模块不应该知道Façade对象。
(3)思考外观模式
①外观模式的本质:封装交互、简化调用。Façade封装了子系统外部和子系统内部多个模块的交互过程,从而简化了外部的调用,通过外观,子系统为外部提供一些高层的接口,以方便它们的使用。它很好的体现了“最少知识原则(DoL)”
②外观模式的目的:外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内部多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。
③Façade表面上看只是把客户端的代搬到了Façade里,但是有本质上的区别。因为Façade位于系统而不是客户端的一侧,它相当于屏蔽了外部客户端和系统内部模块的交互,同时Façade可以被多个客户端调用,实现复用。
④外观是当作子系统对外的接口出现的,虽然可以在这里定义一些子系统没有的功能,但不建议这样做。外观应该是包装己有的功能,而不是添加新的功能。因为有时客户端可能需要绕开Façade,直接调用某个具体模块。而如果在Façade中添加新的功能,但可能最后调用的结果是不同的。
⑤外观可以提供一些缺省的功能实现。
【编程实验】注册公司(通过外观模式提供简化的办理流程)
//结构型模式:外观模式 //场景:注册公司。 #include <iostream> using namespace std; //质检局:Quality Supervision Burean class IQSBureau { public: //到质检局办理组织机构代码证 virtual void orgCodeCertificate() = 0; }; class QSBureau : public IQSBureau { public: void orgCodeCertificate() { cout <<"QSBureau: orgCodeCertificate()" << endl; } }; //工商局:Industrial and Commercial Bureau class IICBureau { public: //检查名字是否冲突 virtual void checkName() = 0; }; class ICBureau : public IICBureau { public: void checkName() { cout << "ICBureau: checkName()" << endl; } }; //税务局: Tax Bureau; class ITaxBureau { public: //办理税务登记证 virtual void taxCertificate() = 0; }; class TaxBureau: public ITaxBureau { public: void taxCertificate() { cout << "TaxBureau: taxCertificate()" << endl; } }; //银行:Bank; class IBank { public: //到银行开户 virtual void openAccout() = 0; }; class Bank : public IBank { public: void openAccout() { cout <<"Bank: openAcount()" << endl; } }; //提供一个外观类(注册公司) class RegisterFacade { private: IICBureau* ic; IQSBureau* qs; IBank* bank; ITaxBureau* tb; public: RegisterFacade() { ic = new ICBureau(); qs = new QSBureau(); bank = new Bank(); tb = new TaxBureau(); } void registerCompany() { ic->checkName(); //工商局 qs->orgCodeCertificate(); //质检局 tb->taxCertificate(); //税务局 bank->openAccout(); //银行开户 } ~RegisterFacade() { delete ic; delete qs; delete bank; delete tb; } }; int main() { //客户端调用 RegisterFacade rf; rf.registerCompany(); return 0; }
2. 外观模式的实现
(1)Façade的实现
对于一个子系统而言,外观类不需要很多,通常可以实现成一个单例。也可以直接把外观中的方法实现成静态方法,这样就可以不需要创建外观对象就可以直接调用,这种实现相当于把外观类当成一个辅助的工具类实现。
(2)Façade可以实现为Interface
通常Façade直接实现为为,但是也可以实现成真正的接口,这里还需要一个Façade的实现,一般可以用工厂方法来获取实现Façade接口的对象。实现为接口的好处是能够有选择地暴露接口,尽量减少模块对子系统对外提供的接口方法。
(3)Façade的方法
一般是负责把客户端的请求转发给子系统内部的各个模块进行处理, Façade的方法实现的只是一个功能的组合调用和转发。但Façade不应参与子系统内的业务逻辑,这会导致子系统必须依赖外观才能被访问,这超出了Façade的本意,也违反了单一职责原则。
3. 外观模式的优缺点
(1)优点
①松散耦合:外观模式松散了客户端与子系统的耦合,让子系统内部的模块能更容易扩展和维护。
②简单易用:客户端不再需要了解子系统内部实现,也不需要与众多的子系统内部模块进行交互,只需要跟外观交互就可以了。相当于外部类为客户端使用子系统提供了一站式服务。
③更好地划分访问的层次。有些方法是对系统外的,有些是系统内部使用的。把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好地隐藏了内部的细节。
(2)缺点:不符合开闭原则,有时可能需要修改外观角色的代码;过多使用Façade容易让人迷惑。
4. 外观模式的使用场景
(1)为一个复杂的模块或子系统提供一个外界访问的接口
(2)子系统相对独立,外界对子系统访问只要黑箱操作即可。如利息计算很复杂,需要深厚的业务知识和扎实的技术水平。但对于客户端来讲,要求输入金额和存期,就可以返回最终的利息。这里可以用外观模式。
(3)当需要构建一个层次结构的子系统时,使用Façade定义子系统中每层的入口点。如果子系统是相互依赖的,可以让它们仅通过Façade进行通讯,从而简化它们的依赖关系。
【编程实验】编译过程的模拟
//结构型模式:外观模式 //场景:编译过程的简化。 //编译器内部所做的事情:词法分析、语法分析、代码优化、代码生成等 //对于客户端来讲,只要给文件路径,只能产生最终的结果。编译器内部所做的 //事情对客户端来说是透明的。 #include <iostream> using namespace std; //扫描器 class Scanner { public: void scan(const char* lpszInput) { cout <<"Scanning: " << lpszInput << endl; } }; //分析器 class Parser { public: void parse(const char* lpszInput) { cout <<"Analysing: " << lpszInput << endl; } }; //代码生成器 class CodeGenerator { public: void codeGen(const char* lpszOutput) { cout <<"Code generate, output to: " << lpszOutput<< endl; } }; //提供一个外观类(编译器) class Compiler { private: Scanner s; Parser p; CodeGenerator c; public: void compile(const char* lpszInput, const char* lpszOutput) { s.scan(lpszInput); p.parse(lpszInput); c.codeGen(lpszOutput); } }; //使用编译系统:这个系统包括扫描器、分析器、生成器。并且提供了一个外观接口 //Compiler类 int main() { //客户端调用 Compiler c; c.compile("123.cpp", "123.exe"); return 0; }
5. 相关模式
(1)外观模式与中介者模式
①这两者很相似,但有本质的区别
②中介者用来封装多个对象之间的交互,多用在系统内部的多个模块之间;而外观的封装是单向的交互,是从客户端访问系统的调用,没有从系统中来访问客户端的调用。
③在中介者模式的实现里面,需要实现具体的交互功能;而外观模式的实现里面,一般是组合调用或转调内部实现的功能,通常外观模式本身并不实现这些功能。
④中介者模式的目的主要是松散多个模块之间的耦合,把这些耦合关系全部放到中介者中去实现;而外观模式的目的是简化客户端的调用。