1、问题
在面向对象系统设计中经常可以遇到以下的两类问题:
1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共 接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子 类实现,达到了多态的目的。这里很容易出现的一个问题 n 多的子类继承自抽象基类,我们 不得不在每次要用到子类的地方就编写诸如 new ×××;的代码。这里带来两个问题 1)客 户程序员必须知道实际子类的名称(当系统复杂后,命名将是一个很不好处理的问题,为了 处理可能的名字冲突,有的命名可能并不是具有很好的可读性和可记忆性,就姑且不论不同 程序员千奇百怪的个人偏好了。),2)程序的扩展性和维护变得越来越困难。
2)还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。这里的意思 为:假设我们在类 A 中要使用到类 B,B 是一个抽象父类,在 A 中并不知道具体要实例化 那一个 B 的子类,但是在类 A 的子类 D 中是可以知道的。在 A 中我们没有办法直接使用类 似于 new ×××的语句,因为根本就不知道×××是什么。 以上两个问题也就引出了 Factory 模式的两个最重要的功能:
1)定义创建对象的接口,封装了对象的创建;
2)使得具体化类的工作延迟到了子类中。
2、模式选择
我们通常使用 Factory 模式来解决上面给出的两个问题。在第一个问题中,我们经常就 是声明一个创建对象的接口,并封装了对象的创建过程。Factory 这里类似于一个真正意义 上的工厂(生产对象)。在第二个问题中,我们需要提供一个对象创建对象的接口,并在子 类中提供其具体实现(因为只有在子类中可以决定到底实例化哪一个类)。
第一中情况的 Factory 的结构示意图为:
图1
上图所示的 Factory 模式经常在系统开发中用到,但是这并不是 Factory 模式的最大威 力所在(因为这可以通过其他方式解决这个问题)。Factory 模式不单是提供了创建对象的接 口,其最重要的是延迟了子类的实例化(第二个问题),以下是这种情况的一个 Factory 的 结构示意图
图2
上图中关键中 Factory 模式的应用并不是只是为了封装对象的创建,而是要把对象的创 建放到子类中实现:Factory 中只是提供了对象创建的接口,其实现将放在 Factory 的子类 ConcreteFactory 中进行。这是图 2 和图 1 的区别。
3、实现
完整代码示例(code)
Factory 模式的实现比较简单,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 VC 6.0 下测试运行)
//代码片断 1:Product.h
//Product.h
#ifndef _PRODUCT_H_
#define _PRODUCT_H_
class Product
{
public:
virtual ~Product() = 0;
protected:
Product();
private:
};
class ConcreteProduct:public Product
{
public:
~ConcreteProduct();
ConcreteProduct();
protected:
private:
};
#endif //~_PRODUCT_H_
//代码片断 2:Product.cpp
//Product.cpp
#include "Product.h"
#include <iostream>
using namespace std;
Product::Product()
{
}
Product::~Product()
{
}
ConcreteProduct::ConcreteProduct()
{
cout<<"ConcreteProduct...."<<endl;
}
ConcreteProduct::~ConcreteProduct()
{
}
//代码片断 3:Factory.h
//Factory.h
#ifndef _FACTORY_H_
#define _FACTORY_H_
class Product;
class Factory
{
public:
virtual ~Factory() = 0;
virtual Product* CreateProduct() = 0;
protected:
Factory();
private:
};
class ConcreteFactory:public Factory
{
public:
~ConcreteFactory();
ConcreteFactory();
Product* CreateProduct();
protected:
private:
};
#endif //~_FACTORY_H_
//代码片断 4: Factory.cpp
//Factory.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
Factory::Factory()
{
}
Factory::~Factory()
{
}
ConcreteFactory::ConcreteFactory()
{
cout<<"ConcreteFactory....."<<endl;
}
ConcreteFactory::~ConcreteFactory()
{
}
Product* ConcreteFactory::CreateProduct()
{
return new ConcreteProduct();
}
//代码片断 5:main.cpp
//main.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
Factory* fac = new ConcreteFactory();
Product* p = fac->CreateProduct();
return 0;
}
代码说明
示例代码中给出的是 Factory 模式解决父类中并不知道具体要实例化哪一个具体的子类 的问题,至于为创建对象提供接口问题,可以由 Factory 中附加相应的创建操作例如 Create***Product()即可。具体请参加讨论内容。
4、讨论
Factory 模式在实际开发中应用非常广泛,面向对象的系统经常面临着对象创建问题: 要创建的类实在是太多了。而 Factory 提供的创建对象的接口封装(第一个功能),以及其 将类的实例化推迟到子类(第二个功能)都部分地解决了实际问题。一个简单的例子就是笔 者开开发 VisualCMCS 系统的语义分析过程中,由于要为文法中的每个非终结符构造一个类 处理,因此这个过程中对象的创建非常多,采用 Factory 模式后系统可读性性和维护都变得 elegant 许多。
Factory 模式也带来至少以下两个问题:
1)如果为每一个具体的 ConcreteProduct 类的实例化提供一个函数体,那么我们可能不 得不在系统中添加了一个方法来处理这个新建的 ConcreteProduct,这样 Factory 的接口永远 就不肯能封闭(Close)。当然我们可以通过创建一个 Factory 的子类来通过多态实现这一点, 但是这也是以新建一个类作为代价的。
2)在实现中我们可以通过参数化工厂方法,即给 FactoryMethod()传递一个参数用以 决定是创建具体哪一个具体的 Product(实际上笔者在 VisualCMCS 中也正是这样做的)。当 然也可以通过模板化避免 1)中的子类创建子类,其方法就是将具体 Product 类作为模板参 数,实现起来也很简单。
可以看出,Factory 模式对于对象的创建给予开发人员提供了很好的实现策略,但是 Factory 模式仅仅局限于一类类(就是说 Product 是一类,有一个共同的基类),如果我们要 为不同类的类提供一个对象创建的接口,那就要用 AbstractFactory 了。