Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2026391
  • 博文数量: 414
  • 博客积分: 10312
  • 博客等级: 上将
  • 技术积分: 4921
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-31 01:49
文章分类

全部博文(414)

文章存档

2011年(1)

2010年(29)

2009年(82)

2008年(301)

2007年(1)

分类: C/C++

2009-01-17 03:35:11

简介

  工厂方法,通常又被称作虚构造函数,给一个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 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
struct StdCreator : public Creator {
    virtual Shape* Create() { return T; }
}

与Loki同行

如果你的工程使用Loki,那么一切都简单了,像下面这样:
// ----------------------------------------------------------------------------------------------------------------------------------
// Shape.h

#ifndef Shape_H_INCLUDED_
#define Shape_H_INCLUDED_

#include
#include
#include

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
#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;
}
阅读(1791) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~