模拟场景
在C++设计模式之简单工厂模式文章中提到简单工厂的不足之处,比如不符合“开闭原则”,工厂类职责重,不易扩展等,比如我们创建对象后,需要从数据库或者文件中获取数据,涉及到较复杂的对象初始过程,又或者需要新增其他的图形类。示例代码如下:
static CGraph * CreateGraph(ENUM_DRAW_TYPE type)
{
CGraph * pGraph = NULL;
switch(type)
{
case EN_DRAW_SQUARE:
{
try
{
//从数据库中获取数据,实现省略
pGraph = new CSquare();
//对pGraph执向的对象进行对象初始化
//具体过程代码省略
}
catch (...)
{
//输出日志打印等操作
}
break;
}
...
//新增圆形对象,修改CGraphFactory工厂类的内容
case EN_DRAW_CIRCLE:
{
pGraph = new CCircle();
break;
}
default:
break;
}
return pGraph;
}
从中可以看出,后续若新增需求必然会对这个工厂类进行修改,修改越多,后续维护难度就越大。下面从简化工厂类职责以及遵守“开闭原则”两个方面进行优化,利用的就是工厂方法模式。
模式基本原理
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化,工厂方法模式让一个类的实例化延迟到其子类。
在工厂方法模式中,我们不再实现面面俱到的工厂类,而是设计一个抽象工厂类,这个抽象工厂类只负责声明必要的创建对象的工厂接口,创建具体的对象工作则由抽象工厂的子类进行实现。一个具体类就对应一个具体工厂类,优化后的UML类图如下:
CTriangleFactory工厂只负责创建CTriangle类,CSquareFactory工厂只负责创建CSquare类,一个具体类对应一个具体工厂。若新增其他的图形类,只需要新增对象的具体类和具体工厂即可,不涉及修改源代码,符合“开闭原则”,类和类之间的耦合性变弱。
具体代码
和简单工厂相比,工厂方法模式只是扩展了工厂类,图形类不涉及修改,下面给出工厂类的代码:
//图形工厂抽象类,负责接口声明
class CGraphFactory
{
public:
virtual CGraph* CreateGraph() = 0;
};
//三角形工厂,负责具体对象创建和必要的初始化
class CTriangleFactory:public CGraphFactory
{
public:
virtual CGraph* CreateGraph()
{
CGraph* pTriangle = new CTriangle;
return pTriangle;
}
};
//正方形工厂,负责具体对象创建和必要的初始化
class CSquareFactory:public CGraphFactory
{
public:
virtual CGraph* CreateGraph()
{
CGraph* pTriangle = new CSquare;
return pTriangle;
}
};
客户端代码:
//选择工厂类
CGraphFactory *pTriFactory = new CTriangleFactory;
if (pTriFactory != NULL)
{
//创建对象
CGraph* pTriGraph = pTriFactory->CreateGraph();
if (pTriGraph != NULL)
{
pTriGraph->Draw();
pTriGraph->Erase();
SAFE_DELETE_PTR(pTriGraph);
}
SAFE_DELETE_PTR(pTriFactory);
}
运行结果:
完成三角形对象创建
绘制三角形
清除三角形
工厂方法模式总结
优点:
工厂方法模式在遇到新增对类时,不会涉及到其他类或者工厂类的修改,只要新增具体类和对应的具体工厂,符合“开放扩展,关闭修改”的原则。
客户端在创建对象时,只需要知道对应的工厂类名字,不必关心具体类名称、如何创建对象以及对象的初始化,屏蔽了一些不必要的过程,简化用户使用。
缺点:
工厂方法模式中除了新增具体类对象,还要新增加与之对应的具体工厂类;从一定程度上增加了系统开销,比如增加了编译时间。
适用场合:
工厂方法模式适用于客户端只关心对象的使用而不关心对象的创建的场景,并且这些对象是同一个系列的,也就是说它们都有共同的基类。