前段时间参加了一个项目的开发,里面有一个程序需要实现这样的功能:
-
程序启动时根据命令行参数提供的接口名称调用不同接口
-
所有接口相互独立,但能抽象出共同行为work,主函数只需负责调用该接口执行work动作
-
接口可能经常变化(增删改),但主函数基本不变
这个程序是这样实现的:
第一版程序实现(详细见附件中的demo_project_v1):
-
定义一个基类,做为所有接口类的父类
//class_base.h
class class_base
{
public:
virtual void work() = 0;
virtual ~class_base();
};
-
每个接口封装成一个独立的类继承自 class_base 并且实现work。
// classA.h
class classA :
public class_base
{
public:
classA(void);
void work();
~classA(void);
};
// classB.h
//与A大致相同
-
主函数根据通过if else语句,根据命令行参数决定调用不同类
//main.cpp
#include "stdafx.h"
#include "class_base.h"
#include "classA.h"
#include "classB.h"
int main(int argc, char* argv[])
{
assert(argc > 1); string class_name = argv[1];
class_base *cbase = NULL;
if (class_name == "classA")
{
cbase = new classA();
}
else if (class_name == "classB")
{
cbase = new classB();
}
else
{
cerr << "error input" << endl;
return -1;
}
cbase->work();
delete cbase;
return 0;
}
程序这样实现确实比较方便,但是存在以下问题:
-
通过if else来判断需要调用哪个接口类,在类少的时候没啥问题,但是在接口慢慢增多的时候,就会出现某一个类要经过N次判断后才能确定被调用(前面说过,接口是会经常增加的),N随接口个数增加而线性增加
-
每次新增类都需要修改main.cpp,增加该类的头文件,并且增加if else分枝,致使main函数越来越长,编译也越来越慢
-
修改了接口类的头文件,就需要重新编译main.cpp,当接口类比较多时编译比较慢(指在VS下,如果是自己写Makefile则不会有这个问题)
但是当时由于时间的关系,这个程序就将就这样了。在之后相对比较有空时间,我开始想,下次碰上这种问题应该怎么解决,但是一直没找到好的解决方法。
直到最近,学习了设计模式,才发现,原来前人早已总结出一个针对这种问题的设计模式。没错,就是标题里的“抽象工厂”模式。
这里简单介绍一下抽象工厂(百度上找到的)
意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
适用性:
一个系统要独立于它的产品的创建、组合和表示时
一个系统要由多个 产品系列中的一个来配置时
当你要强调一个系列相关的产品对象的设计以便进行联合使用时
简单地说,提供一个创建接口类的接口,而无需指定它们的具体类(即可以不需要知道它们的实现)。下面介绍具体实现
第二版程序实现(详细见附件中的demo_project_v3)
首先,定义一个抽象工厂(工厂类接口)IFACTORY
//class_base.h
class IFACTORY
{
public:
virtual class_base *create_class() = 0;
public:
virtual ~IFACTORY(void);
};
为每个类实现一个类工厂(继承于抽象工厂)
//FactoryA.h
#include "class_base.h"
class FactoryA :
public IFACTORY
{
public:
FactoryA(void);
class_base *create_class();
~FactoryA(void);
};
//FactoryA.cpp
#include "FactoryA.h"
#include "classA.h"
class_base *FactoryA::create_class()
{
return new classA();
}
这样主函数里面只需要知道类工厂定义即可,无需知道类的定义
//main.cpp
#include "stdafx.h"
#include "class_base.h"
#include "FactoryA.h"
#include "FactoryB.h"
int main(int argc, char* argv[])
{
assert(argc > 1);
map<string, IFACTORY*> mpClasses;
mpClasses["classA"] = new FactoryA();
mpClasses["classB"] = new FactoryB();
IFACTORY *ClassFactory = mpClasses[argv[1]];
if (ClassFactory == NULL)
{
cerr << "error input" << endl;
return -1;
}
class_base *cbase = ClassFactory->create_class();
cbase->work();
delete cbase;
return 0;
}
经过引入抽象工厂后,
由于不知道接口类的实现,接口类的头文件修改就不会再导致main.cpp重新编译了
并且,由于使用了map来选择类工厂,比较次数相比以前的if else大大减少
引用抽象工厂后,问题3跟问题1都解决了,但是问题2似乎没解决,新增一个类之后仍然需要修改main.cpp的mpClasses注册一个新的工厂类。
但是,经过观察,我们可以看到,其实每个类工厂都代码基本是一样的,所以,我们可以预先写好多个类工厂(类工厂1,类工厂2, …),通过配置指定不同字符串对应不同类工厂。当需要新增类时只需要修改一下配置文件,并且修改一个相应类工厂的实现即可。(当然有更好的解决方法,留等读者自己思考) (如果懒得思考可以直接看demo_project_v4的实现)
本文介绍了根据字符串生成类的一种实现方法,实际上还有更多实现方法(如反射)之类,各位读者有兴趣可以上网查。(完)
后记:
demo_project_v3的实现有几个问题
1 每新增一个类需要copy大量重复无用的代码(类工厂)
2 增加新类必须修改main.cpp增加新的类工厂(用文章结尾的方法,则需修改配置文件和类工厂)
demo_project_v4是解决这个问题的方案
demo_project_v5 是demo_project_v4 的改进,放弃了抽象工厂模式,去掉了沉重的工厂负担。