自己有过组装电脑的人都知道,需要到电子市场去购买CPU、主板、硬盘、内存、显示器、光驱等配件。这个方案是好,但需要对各种配件都比较熟悉,这样才能选择最合适的配件,而且还需要考虑配件之间的兼容性。还有一个方案,就是到电子城,找一家专门组装电脑的公司,把自己的要求提出来,然后等着提电脑就好了。我们不需要关心要购买什么样的配件,也不需要知道电脑是如何组装起来的,所有这些操作都交给电脑组装公司,由它代为完成。在设计模式中,也存在一种类似的模式,完成某个功能需要调用其它多个模块,由外观类去调用这些模块来完成功能,这样客户端就不用跟系统中的多个模块交互,也不用知道那么多模块的细节了,称之为外观模式。
1、外观模式概述
在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似电脑组装公司一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互。外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“电脑组装公司”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。在外观模式中,那些需要交互的业务类被称为子系统(Subsystem)。如果没有外观类,那么每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大,而引入外观类之后,客户类只需要直接与外观类交互,客户类与子系统之间原有的复杂引用关系由外观类来实现,从而降低了系统的耦合度。
外观模式示意图
外观模式:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |
外观类将客户类与子系统的内部复杂性分隔开,使得客户类只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道。它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。
外观模式结构图
Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。外观角色只负责委派操作,而不参与子系统的业务逻辑,为什么呢?因此外观对象只是提供一个访问子系统的一个路径而已,它不应该也不能参与具体的业务逻辑,违背了"单一原则",导致外观对象既负责业务处理,也负责委托操作。
SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
外观模式的主要目的在于降低系统的复杂程度,在面向对象软件系统中,类与类之间的关系越多,不能表示系统设计得越好,反而表示系统中类之间的耦合度太大,这样的系统在维护和修改时都缺乏灵活性,因为一个类的改动会导致多个类发生变化,而外观模式的引入在很大程度上降低了类与类之间的耦合关系。引入外观模式之后,增加新的子系统或者移除子系统都非常方便,客户类无须进行修改(或者极少的修改),只需要在外观类中增加或移除对子系统的引用即可。从这一点来说,外观模式在一定程度上并不符合开闭原则,增加新的子系统需要对原有系统进行一定的修改,虽然这个修改工作量不大。
外观模式中所指的子系统是一个广义的概念,它可以是一个类、一个功能模块、系统的一个组成部分或者一个完整的系统。子系统类通常是一些业务类,实现了一些具体的、独立的业务功能,其典型代码如下:
//子系统A
class SubSystemA
{
public:
void MethodA()
{
//业务实现代码
}
};
//子系统B
class SubSystemB
{
public:
void MethodB()
{
//业务实现代码
}
};
//子系统C
class SubSystemC
{
public:
void MethodC()
{
//业务实现代码
}
};
在引入外观类之后,与子系统业务类之间的交互统一由外观类来完成,在外观类中通常存在如下代码:
//外观类
class Facade
{
private:
//维持子系统的引用
SubSystemA * m_pSubSystemA;
SubSystemB * m_pSubSystemB;
SubSystemC * m_pSubSystemC;
public:
//创建各子系统对象
Facade()
{
m_pSubSystemA = new SubSystemA();
m_pSubSystemB = new SubSystemB();
m_pSubSystemC = new SubSystemC();
}
//销毁子系统对象
~Facade()
{
delete m_pSubSystemA;
m_pSubSystemA = NULL;
delete m_pSubSystemB;
m_pSubSystemB = NULL;
delete m_pSubSystemC;
m_pSubSystemC = NULL;
}
//委派操作
void Method()
{
m_pSubSystemA->MethodA();
m_pSubSystemB->MethodB();
m_pSubSystemC->MethodC();
}
};
由于在外观类中维持了对子系统对象的引用,客户端可以通过外观类来间接调用子系统对象的业务方法,而无须与子系统对象直接交互。引入外观类后,客户端代码变得非常简单,典型代码如下:
int main()
{
Facade * pFacade = new Facade()
pFacade->Method();
delete pFacade;
pFacade = NULL;
}