代码下载:http://download.csdn.net/detail/jzp12/4371049
我将通过对外观模式 (Facade Pattern) 的讲解来开始我们对设计模式的学习。也许在过去你就已经使用到了外观设计模式只是你并未曾注意到。
在这一章里将包含以下的一些内容:
l 什么是外观设计模式 (Facade Pattern) 及其用途
l 指出 Facade 模式的主要功能
l 提出 Facade 模式的一些变化
Facade 模式的基本介绍
在 GoF 的《设计模式》一书中提到,使用 Facade 模式的目的是:给子系统的系列接口提供一个统一的外部接口。使用 Facade 模式就是定义一个 Facade 接口,她是一个可以使原来的子系统更加容易被我们使用的高级接口。
也就是说,我们需要和一个比当前系统所提供的方法要更加难的系统进行交互,或者说我们需要用某种特定的方式来使用当前系统 ( 比如把一个 3D 的绘图程序作为 2D 的绘图程序来使用 ) 。我们可以建立这样的方法因为我们只需要使用到当前系统的一部分功能。
正式学习 Facade 模式
曾经,我在一个大型的工程制造工厂里负责工程承包的工作。在我工作的第一天,这个项目的技术带头人不在。但是,我不可能白拿薪水,客户想给我找点事情干,尽管这些事情毫无意义。难道,你没有遇见过这样的情况吗?
因此,该项目组的一个人还真给我找了点事做。她说:“你将会学习到我们会使用到的 CAD/CAM 系统,也许现在你就可以开始学习了。就从这些手册开始吧!”然后,她把我带到一大堆的文档前。不是我夸张,有 8 英尺这么长的手册摆在书架上等着我去阅读,而且这些手册都是 8.5 × 11 英寸大小,还是小号字印刷的!这真是一个复杂的系统。
那么,我,或者说我和你,或者更多人,假如我们将会在一个项目中用到刚才的这个 CAD/CAM 系统,我们该怎么办?是我们每个人都学习到底如何使用这个系统吗?还是说我们先把我们要用到功能提出来,然后由一个人来编写某些特定的接口来使用这个系统,而剩下的人只需要知道如何使用这些特定的接口就可以了呢?
这个编写接口的人将决定我和我们项目组的其他人将如何使用这个系统,将决定我们用什么样的 API 来完美的完成工作。她将会根据我们的需要创建一个或者多个的类 (Facade class) 。我和其他同事就只使用她创建的这些类而不用去学习这整个复杂的系统了。
这种方式只适合在我们只用到这个系统的一部分功能或者只用某种特定方式来使用这个系统的时候。如果系统中的所有功能都要被用到,那么更好的设计方案将会是考虑当前系统是否是最值得我们使用的。
这就是外观设计模式 (Facade Pattern) 。她让我们可以更方便地使用那些相当复杂的系统。虽然我们有一个非常复杂的系统,但是,我们仅仅需要其中的一部分功能,或者我们以某个特定方式来使用这些功能。怎样,通过 Facade 模式的使用,我们将会得到一个非常易用或者更满足我们需要的系统。
大多数工作仍然将会被底层的原始系统来完成。前面讲到的我们自己编写的 Facade class 将提供一些很容易理解的方法供我们使用。这些方法使用底层的原始的系统来完成我们新定义的功能。
Facade 模式不仅仅可以用来创建更简单的方法调用,还可以用来减少客户对象需要操纵的对象的个数。例如:假设我有一个 Client 类的对象,它必须打开一个 Database ,并从中取得一个 Model 。然后通过 Model 查询得到一个 Element 。最后, Client 还要从 Element 里面得到自己需要的信息。这肯定是一个很复杂的过程。如果,我们建立一个 DatabaseFacade 的中间类, Client 通过它来完成上述的一系列工作,这一切将变得简单的多。请看下面的 UML 图。
如果我们的 Facade class 是无状态的类 ( 也就是说它里面不保存任何和状态有关的量 ) ,一个 Facade 的对象可以同时被多个其他的对象使用。随后,在 21 章,我们将学习如何通过使用 Singleton 模式和 Double-Checked Locking 模式来实现这个功能。
设想,除了使用当前系统内的功能以外,我们还提供一些新的功能-比如,把所有程序调用过程记录到一个事务里面。这样,我们所使用的功能超出了当前系统的功能。
这种情况下,我们可以通过向 Facade 类里添加其他的一些方法 (method) 来扩展其功能。这仍然是 Facade 模式,只是她原有的功能得到扩展。我们的主要目标是简化方法的调用因为我们并不希望客户端程序知道调用哪些额外的方法-这一切都通过 Facade 来完成了。
Facade 模式给我们一个普遍的使用方法。在本模式中的 Facade 类的实质就是我们为客户端创建新的接口,而不是去继续使用原有的接口。我们可以这样做,是因为我们不需要使用原始系统中的所有方法。
Facade 模式还可以用来隐藏或封装原来的系统。 Facade 类可以把原来的系统当成私有变量。这样,原始系统就仅仅和 Facade 类有联系而不会暴露给 Facade 类的使用者。
正如下面将要提到的,我们有很多理由来封装系统:
l 追踪系统使用 -通过限制所有对系统功能的调用必须由 Facade 类来完成,我们可以非常方便地监视系统使用状况。
l 在系统间进行切换 -有可能在将来我们所使用的系统会有所改变。通过把原始系统当成 Facade 类的私有成员变量 (private member) 来处理,我们可以很轻易的切换我们所使用的系统。当然,也许仍然有巨大的工作量,但是至少,我只需要改变一个地方 (Facade 类 ) 。
总结
外观模式 (Facade Pattern) 名称的得来是因为她在原始系统的前面建立了一个新的接口 (Facade 类 ) 。
我们将在以下的情况中使用到 Facade 模式:
l 当不需要使用一个复杂系统的所有功能时。创建一个类来包含所有使用这个系统的规则、方法。通常,我们会用到一个原始系统的子系统,新创建的类的接口 (API) 将会比原始系统的接口 (API) 简单得多。
l 当想要封装或隐藏原始系统时。
l 当想要使用原始系统的某些功能并添加新功能时。
l 当编写新类的花费要低于项目组每个人都学习如何使用原始系统或者低于在后期维护时的投入的时候。根据上面的资料,我用C++实现了facade模式,类图如下:
#ifndef FACADE_PATTERN_H
#define FACADE_PATTERN_H
#define DEFAULT_MODEL_NUM (2)
class Model;
class Element;
//Database
class Database{
public:
//constructor
Database();
//destructor
~Database();
void init(void);
Model *getModel(int nIndex);
Element *queryElement(const Model *pModel);
private:
Model *m_pModel[DEFAULT_MODEL_NUM];
Element *m_pElement[DEFAULT_MODEL_NUM];
};
//Model declare
class Model{
public:
Model(int nIndex);
int getIndex(void);
private:
int m_nIndex;
};
//Element declare
class Element{
public:
Element(int nIndex);
int getIndex(void);
private:
int m_nIndex;
};
//FacadeClass declare
class FacadeClass{
public:
FacadeClass();
~FacadeClass();
//only one interface
void run(void);
private:
Database *m_pDatabase;
};
#endif
FacadePattern.cpp
// FacadePattern.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "FacadePattern.h"
#include <iostream>
#include <string>
using namespace std;
Database::Database()
{
int i = DEFAULT_MODEL_NUM - 1;
while (i >= 0){
m_pModel[i] = NULL;
m_pElement[i] = NULL;
--i;
}
cout<<"Database construct()"<<endl;
init();
}
void Database::init(void)
{
int i = DEFAULT_MODEL_NUM - 1;
while (i >= 0){
m_pModel[i] = new Model(i);
m_pElement[i] = new Element(i);
--i;
}
cout<<"Database init()"<<endl;
}
Database::~Database()
{
int i = DEFAULT_MODEL_NUM - 1;
while (i >= 0){
if (NULL != m_pModel[i]){
delete m_pModel[i];
m_pModel[i] = NULL;
}
if (NULL != m_pElement[i]){
delete m_pElement[i];
m_pElement[i] = NULL;
}
--i;
}
cout<<"Database destructor()"<<endl;
}
Model *Database::getModel(int nIndex)
{
Model * pModel = NULL;
do
{
if (nIndex < 0 || nIndex >= DEFAULT_MODEL_NUM)
{
break;
}
if(NULL != m_pModel[nIndex]){
pModel = m_pModel[nIndex];
}
} while (0);
cout<<"Database getModel()"<<endl;
return pModel;
}
Element *Database::queryElement(const Model *pModel)
{
Element *pElement = NULL;
int i = DEFAULT_MODEL_NUM - 1;
while (i >= 0){
if (pModel == m_pModel[i]){
pElement = m_pElement[i];
}
--i;
}
cout<<"Database queryElement()"<<endl;
return pElement;
}
Model::Model(int nIndex)
{
m_nIndex = nIndex;
cout<<"Model construct()"<<endl;
}
int Model::getIndex(void)
{
return m_nIndex;
}
Element::Element(int nIndex)
{
m_nIndex = nIndex;
cout<<"Element construct()"<<endl;
}
int Element::getIndex(void)
{
return m_nIndex;
}
FacadeClass::FacadeClass():m_pDatabase(NULL)
{
cout<<"FacadeClass construct()"<<endl;
}
FacadeClass::~FacadeClass()
{
cout<<"FacadeClass destructor()"<<endl;
if (NULL != m_pDatabase)
{
delete m_pDatabase;
m_pDatabase = NULL;
}
}
void FacadeClass::run()
{
do
{
Database *pdatabase = NULL;
m_pDatabase = pdatabase = new Database();
if (NULL == pdatabase)
{
break;
}
Model * pModel = NULL;
pModel = pdatabase->getModel(0);
if (NULL == pModel)
{
break;
}
Element *pElement = NULL;
pElement = pdatabase->queryElement(pModel);
if (NULL != pElement)
{
cout<<"Element index = "<<pElement->getIndex()<<endl;
}
} while (0);
}
int main(int argc, char* argv[])
{
FacadeClass *pfade = NULL;
pfade = new FacadeClass();
if (NULL != pfade)
{
pfade->run();
delete pfade;
}
return 0;
}