FACTORY 与 ABSTRACT FACTORY模式 —— 号令秦姬驱赵女,艳李秾桃临战场

 

     你应该猜到了,我们此次的例子是女将军的战争场面。战争一般说来肯定离不开男人,所以男性将军也需要保留。但女将军的作战处理可能和男性并不相同,所以我们会她们单独的提供一个类,而女将军和男将军会有一个共同的接口。先来看类图:

     假设在没有继承时有这么一个函数:
void War()
{
General *pGeneral = new General();
pGeneral->Ready();
pGeneral->Advance();
pGeneral->Assault();
delete pGeneral;
}

     在有了继承的情况下,该函数就可能会变成以下的情况:
void War(string strType)
{
General *pGeneral ;
if ( strType == “GirlGeneral”)
{
pGeneral = new GirlGeneral();
}
else if ( strType == “ManGeneral”)
{
pGeneral = new ManGeneral();
}
pGeneral->Ready();
pGeneral->Advance();
pGeneral->Assault();
delete pGeneral;
}

     想象一下,创建对象的地方很可能并不只一处。那么你就可能在很多地方看到类似的代码,想象一下如果我们再增加一些少年将军,神仙将军一类的基类,那你是不是需要一个一个找到这样的函数去改?(好多时候遇到这样的情况,开发人员怪用户提的要求变态,确很少考虑自己的程序扩展性不强。)为了避免这种情况,最简单的方法就是将类的创建过程独立为一个单独的类,如下:
class GeneralFactory
{
public:
static General *CreateGeneral(string strType)
{
if ( strType == “GirlGeneral”)
{
pGeneral = new GirlGeneral();
}
else if ( strType == “ManGeneral”)
{
pGeneral = new ManGeneral();
}
}
};

     这样我们的创建代码被放到了一个统一的地方,在类进行扩展的时候,就可以方便的进行修改了。也避免了代码的冗余。不过这个方法并没有归于GOF的设计模式中,我们以下将谈Factory模式。
    再考虑一个问题,我们的War是出现在一个类中,如我们有一个军队类,而军队分为男军和女军。男军的将领肯定是男将军,而女军的将领肯定是女将军。而且General类只会在军队类中用到。那我们还有没有必要写一个创建类呢?答案是否定的。我们先来看类图:

     那么这时候,我们就可以将CreateGeneral放到Army类中,而通过它的子类来实现它。好的,我们还是先看看具体的代码,这样有助于你理解。
#include 
#include 
using namespace std;
class General
{
public:
virtual void Ready() = 0;
virtual void Advance() = 0;
virtual void Assault() = 0;
};
class GirlGeneral : public General
{
private:
string m_strName;
public:
GirlGeneral(string strName):m_strName(strName){}
void Ready()
{
cout << " 女将军 " << m_strName << "正在准备!"<< endl;
}
void Advance()
{
cout << " 女将军 " << m_strName << "正在前进!"<< endl;
}
void Assault()
{
cout << " 女将军 " << m_strName << "正在攻击!"<< endl;
}
};
class ManGeneral : public General
{
private:
string m_strName;
public:
ManGeneral(string strName):m_strName(strName){}
void Ready()
{
cout << " 将军 " << m_strName << "正在准备!"<< endl;
}
void Advance()
{
cout << " 将军 " << m_strName << "正在前进!"<< endl;
}
void Assault()
{
cout << " 将军 " << m_strName << "正在攻击!"<< endl;
}
};
class Army
{
public:
void war(string strName)
{
cout << "战斗步骤" << endl;
//此处调用虚函数,事实上调用的是子类的方法
General *pGeneral = CreateGeneral(strName);
pGeneral->Ready();
pGeneral->Advance();
pGeneral->Assault();
cout << endl;
}
//此处定义一个创建Geraral的类,而不具体实现
virtual General* CreateGeneral(string strName) = 0;
};
class GirlArmy : public Army
{
public:
General *CreateGeneral(string strName)
{
return new GirlGeneral(strName);
}
};
class ManArmy : public Army
{
public:
General *CreateGeneral(string strName)
{
return new ManGeneral(strName);
}
};
int main(int argc, char* argv[])
{
GirlArmy gArmy;
gArmy.war("姽婳将军");
ManArmy mArmy;
mArmy.war("笨蛋将军");
return 0;
}


     代码比较简单,无需太多解释,需要注意的就是在Army类中注释过的地方就可以了。
    下面我们来看GOF对于Factory模式的定义:定义一个用于创建对象的接口,让子类决定实例化哪个类。Factory Method使一个类的实例化延迟到其子类。
    GOF书中提供的一个例子机具代表性,是Factory的典型用法,我们来看看。

     这是我们在文档视中看到的典型做法,在MFC中也可以找到。Application并不是自己创建文档,而是通过在子类中重载CreateDocument来实现。Factory模式,我们的讲解先到此。
    我们再来考虑一个问题:我们的Army类存在与General同样的问题,生成对象的时候需要考虑到它的类型。那我们是否需要象上面提到的GeneralFactory一样,为它也提供一个类厂呢?接着扩展我们的想象,由于女将的加入,她们使用的兵器,坐骑以至战争场景等内容也许都需要做相应的扩展,那么我们是否需要这么多类都提供一个类厂呢?我们可以注意到所有这些扩展的类都有相似的功能都是为女性提供的,那么我们就可以给它们提供一个统一的类来实现创建过程。该方式就是Abstract Factory模式,我们首先来看看该模式。
    Abstract Factory模式的定义是:提供一个创建一系列产品或者相互依赖对象的接口,而无需调用处指定具体的类。接着看看Abstract Factory的类图:

     图中AbstractFactory是一个抽象接口,定义了创建不同产品的虚拟函数。而ConcreteFactory1和ConcreteFactory2是我们用到的具体的生成产品的类,图上标的虚线表示它们具体要生成的那个类的对象。而AbstractProcutA和AbstractProcutB是抽象产品接口,相当于我们的Army和General接口。而ConcreteProcutA1和ConcreProcutB1是具体的产品类,我们的例子中相当于GirlArmy和GirlGeneral。而ConcreteProcutA2和ConcreProcutB2就相于ManArmy和ManGeneral了。
    接着看我们的例子,此处就不画类图了。直接看看代码:
class AbstractFactory
{
public:
virtual General* CreateGeneral() = 0;
virtual Army* CreateArmy() = 0;
};
class GirlFactory : public AbstractFactory
{
public:
General *CreateGeneral(string strName)
{
return new GirlGeneral(strName);
}
Army *CreateArmy()
{
return new GirlArmy();
}
};
class ManFactory : public AbstractFactory
{
public:
General *CreateGeneral(string strName)
{
return new ManGeneral(strName);
}
Army *CreateArmy()
{
return new ManArmy();
}
};

     这样在我们的例子中的函数:
void war(string strName)
{
cout << "战斗步骤" << endl;
General *pGeneral = CreateGeneral(strName);
pGeneral->Ready();
pGeneral->Advance();
pGeneral->Assault();
cout << endl;
}

    可能变为
void war(AbstractFactory *pFactory,string strName)
{
cout << "战斗步骤" << endl;
General *pGeneral = AbstractFactory ->CreateGeneral(strName);
pGeneral->Ready();
pGeneral->Advance();
pGeneral->Assault();
cout << endl;
}

     Abstract Factory在现实开发中用的比较多的情况是在处理数据库的时候,如果你的程序需要同时支持oracle和SQL Server数据库,那样几乎所有的数据操作类都需要两套代码。那么使用Abstract Factory模式,只需通过配置文件生成不同的创建函数,就可以实现不同数据库的实现了。
    事实上Abstract Factory相当于Factory模式的扩展,都是基类定义一个创建对象的方法,而由子类来实现。
    Abstract Factory模式也可以使用PROTOTYPE(原型)模式来实现,PROTOTYPE的定义是:用原型实例来创建对象的类。说白了就是通过拷贝构造函数来实现新类的创建。这里就不多讲了。
    还有Builder模式,其实和Abstract Factory很相似,但它强调的内容不同,它强调的是一步一步要创建对象,也就是说它会帮助对象创建一些内容。而Abstract Factory是直接返回对象。
    接着考虑,我们想把我们的工厂类作成一个单件类型,这样的话,由于工厂类只有一个对象,我们就容易对工厂对象所要创建的对象做些控制,如可以限定对象的生成数,控制对象的生成时机等。我们不能象上节一样直接把CreateGeneral定义成static型,因为这样就没有动态的特性。那用什么方法呢?还是看代码:
class GirlFactory : public AbstractFactory
{
private:
static GirlFactory *instance;
GirlFactory(){}
public:
static GirlFactory* GetInstance()
{
if ( instance == NULL )
{
instance = new GirlFactory();
}
return instance;
}
General *CreateGeneral(string strName)
{
return new GirlGeneral(strName);
}
Army *CreateArmy()
{
return new GirlArmy();
}
};
GirlFactory *GirlFactory::instance = NULL;

     我们的类工厂中,构造函数GirlFactory被设置成了私有成员函数,这样只有它本身才能调用。我们再填加一个静态成员变量instance,这样保证了只有一个对象。而后再定义一个静态函数GetInstance,它返回所需要的类对象。
    这样我们对类对象的调用就成了以下的形式:
    GirlFactory *pGirlFactory = GirlFactory::GetInstance();
     好了,这就是单件模式了。这次就先讲这么多了。
参考书目:
1,	设计模式——可复用面向对象软件的基础(Design Patterns ——Elements of Reusable Object-Oriented Software) Erich Gamma 等著 李英军等译  机械工业出版社
2,	Head First Design Patterns(影印版)Freeman等著 东南大学出版社
3,	道法自然——面向对象实践指南    王咏武 王咏刚著  电子工业出版社
4,      红楼梦(脂批汇校本) —— 网上找到的电子档


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=655861

posted on 2006-04-17 21:22  Blackie  阅读(562)  评论(0编辑  收藏  举报

导航