本文主要是介绍工厂方法:鸡肋方法?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
合理的使用工厂设计模式,不要使其成为鸡肋方法。《设计模式》一书中描述的工厂方法,实在是叫人母语,鸡肋的很。
通俗的描述一下,工厂,就是生产产品的地方,既然是工厂,那么就不能生产一个产品,而是生产一系列产品,不然就没有必要了。投入原料,经过加工,产出产品。new方法本是最原始的单一产品制造工厂了,为了保证产品被确实制造出来,构造函数不出异常,有些实现中用了二段构造,先new再construct。
下文是转载的一篇比较实用的工厂方法的应用。
简介
工厂方法,通常又被称作虚构造函数,给一个ID,就可以产出一个对象。
了解设计模式的人都知道这样一份臭名昭著的实现:
// ---------------------------------------------------------------------------------
// Shape.h
enum ShapeType { ShapeType_Line, ShapeType_Triangle, };
class Shape {
public:
virtual ~Shape() {}
virtual void Draw() = 0;
static Shape* Create( ShapeType shapeType );
};
// ---------------------------------------------------------------------------------
// Shape.cpp
#include "Line.h"
#include "Triangle.h"
Shape* Shape::Create( ShapeType shapeType ) {
switch ( shapeType ) {
case ShapeType_Line: return new Line;
case ShapeType_Triangle: return new Triangle;
default: return NULL;
}
}
缺点
一个显著的缺点是新类型难于扩展:
如果你新增了一个类型Rectangle,就要改动
1. Shape.h - 在enum里加一个类型ID
2. Shape.cpp - 增加一个头文件#include,增加一个switch case分支
对于Shape.h的改动的影响面可能超乎你的想象:
1. 所有派生于Shape的类实现,因为包含到Shape.h,将不得不重新编译
2. 所有Shape类的用户同样也得重新编译
另外这种做法无法封装成库,因为我们不可能因为用户新增了类型而去修改lib文件。
所以要具备可扩展性,用户新增的类型就不能影响Shape.h和Shape.cpp。要怎做呢?Loki库给了我们很好的展示,基本做法像下面这样:
解决方案
// ---------------------------------------------------------------------------------
// Shape.h
typedef const char* ShapeType;
typedef Shape* (*Creator)();
class Shape {
public:
virtual ~Shape() {}
virtual void Draw() = 0;
};
class ShapeFactory {
public:
static ShapeFactory& Instance() {
static ShapeFactory instance;
return instance;
}
Shape* Create( ShapeType shapeType );
bool RegisterShape( ShapeType shapeType, Creator creator );
private:
ShapeFactory() {}
Map< ShapeType, Creator > shapeCreators;
};
// ---------------------------------------------------------------------------------
// Shape.cpp
Shape* ShapeFactory::Create( ShapeType shapeType ) {
Creator creator = shapeCreators.Find( shapeType );
if ( creator == NULL ) { return NULL; }
return creator();
}
bool RegisterShape( ShapeType shapeType, Creator creator ) {
Creator creator = shapeCreators.Find( shapeType );
if ( creator == NULL ) {
shapeCreators.Insert( shapeType, creator );
return true;
}
return false;
}
// ---------------------------------------------------------------------------------
// Line.cpp
namespace {
Shape* Create() { return new Line; }
const bool RegisterShape__ = ShapeFactory::Instance().RegisterShape( "Line", Create );
}
// ---------------------------------------------------------------------------------
// Triangle.cpp
namespace {
Shape* Create() { return new Triangle; }
const bool RegisterShape__ = ShapeFactory::Instance().RegisterShape( "Triangle", Create );
}
如果增加新类,只需在新类里增加类似上面这段就行了。可以把这断代码做成宏放在Shape.h里面。
#define REGISTER_SHAPE( className ) \
namespace { \
Shape* Create() { return new className; } \
const bool RegisterShape__ = ShapeFactory::Instance().RegisterShape( #className, Create );\
}
细节讨论
1. 关于ID
a)可以采用任何你喜欢的类型做为ID,但必须保证其唯一性。字符串通常是个不错的选择(如果你用整数,那么最好用GUID生成器产生,因为你并不知道其他子类的ID是什么)。
b)采用const char*作为ID,有一定的风险:如果你的类打算从文件中序列化产生,那么序列化出来的字符串并不位于全局静态存储区,直接对其做 == 操作会产生错误。解决方案是使用string替代const char*。
2. ShapeFactory是个单件,关于单件的详细讨论,可以参考:单件和仿单件的6种做法
3. 创建函数typedef Shape* (*Creator)();
此乃上述做法之关键:首先将ID映射为创建函数,再由此函数产生对象。将函数保存起来并于稍后调用,这也是Command模式之核心观念。(《C++设计新思维》p100)
一些替代的方案可供考虑:
a)传统方案,用函数对象代替函数指针:
struct Creator {
Shape* operator() {
return new Line;
}
}
b)使用模板以避免创建子类(详见《设计模式》p74-75):
struct Creator {
virtual Shape* Create() = 0;
}
template <class T>
struct StdCreator : public Creator {
virtual Shape* Create() { return T; }
}
与Loki同行
如果你的工程使用Loki,那么一切都简单了,像下面这样:
// ----------------------------------------------------------------------------------------------------------------------------------
// Shape.h
#ifndef Shape_H_INCLUDED_
#define Shape_H_INCLUDED_
#include <string>
#include <loki/Singleton.h>
#include <loki/Factory.h>
class Shape {
public:
virtual ~Shape() {}
virtual void Draw() = 0;
};
typedef Loki::SingletonHolder< Loki::Factory< Shape, std::string > > ShapeFactory;
#define REGISTER_SHAPE( className ) \
namespace { \
Shape* Create() { return new className; } \
const bool RegisterShape__ = ShapeFactory::Instance().Register( #className, Create ); \
}
#endif // Shape_H_INCLUDED_
// ----------------------------------------------------------------------------------------------------------------------------------
// Line.h
#ifndef Line_H_INCLUDED_
#define Line_H_INCLUDED_
#include "Shape.h"
class Line : public Shape {
public:
virtual void Draw();
};
#endif // Line_H_INCLUDED_
// ----------------------------------------------------------------------------------------------------------------------------------
// Line.cpp
#include <iostream>
#include "Line.h"
REGISTER_SHAPE( Line )
void Line::Draw() {
std::cout << "Line::Draw()" << std::endl;
}
// ----------------------------------------------------------------------------------------------------------------------------------
// main.cpp
#include "Shape.h"
int main() {
Shape* line = ShapeFactory::Instance().CreateObject( "Line" );
line->Draw();
delete line;
return 0;
}
《设计模式》chapter3.3
《C++设计新思维》chapter8
这篇关于工厂方法:鸡肋方法?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!