现在的位置: 首页 > 综合 > 正文

ObjectFactories

2018年03月30日 ⁄ 综合 ⁄ 共 2909字 ⁄ 字号 评论关闭

假设你在编写一个简单的绘图程序,该程序允许使用者编辑简单的向量图形(包括线、圆、多边形等)。采用面向对象风格,定义一个 abstract class  shape,让所有图形都派生于它:

class Shape
{
public:
       virtual void Draw() const = 0;
       virtual void Rotate(double angle) = 0;
       virtual void Zoom(double zoomFactor) = 0;
};

可以定义一个包含复杂图形的 Drawing class 用于保存着一个集合,其内都是 pointer to Shape,并提供某些操作,将复杂图形视为一个整体来处理。

两个典型的操作:(1)将图形保存为文件,(2)从先前保存的文件中取出图形

class Drawing
{
....
public:
       void Save(std::ofstream& outFile);
       void Load(std::ifstream& inFile);
};
void Drawing::Save(std::ofstream& outFile)
{
      write drawing header
      for (each element in the drawing)
      {
              (current element)->save(outFile);
      }
}

当从文件装载图形时,我们必须知道正在装在图形的类型(线、圆...),一个简单的做法是,要求每一个从 Shape 派生的对象都在文件起始处存一个整数标识符。每个对象都一个自己独一无二的 ID。文件的去读将会像这样:

namespace DrawingType
{
const int 
         LINE = 1,
         POLYGON = 2,
         CIRCLE = 3;
};
void Drawing::Load(std::ifstream& inFile)
{
         // error handling omitted for simplicity
         while (inFile)
         {
              // read object type
              int drawingType;
              inFile >> drawingType;
              // create a new empty object
              Shape* pCurrentObject;
              switch (drawingType)
              {
              case LINE:
		pCurrentObject = new Line;
	      break;
              case POLYGON:
                pCurrentObject = new Polygon;
              break;
              ...
              }
              // read the object's contents by invoking a virtual fn
              pCurrentObject->Read(inFile);
              add the object to the container
         }
}

它基于型别标记(type tag)执行了 switch 语句,因而带有 switch 语句的相应缺点。

它在一个源码文件中收集所有关于Shape 派生类的相关信息;对于所有可能的图形, Drawing::Save 的实现文件都必须包含其头文件,造成改变以依存和维护上的瓶颈。
它难以扩充。扩充时必须增加图形 class、图形的 type tag、图形相应的 switch 语句。

希望产生的 object factory:它能完成我们的工作,但不存在以上缺点。拿掉 switch 语句,使 Line 的生成语句可以放在 Line 的实作文件中。“工厂”维护着一个由函数指针组成的集合,所有函数指针所指向的函数都具有 Shape* CreateConcreteShape();这样的形式。此外 ID 和 “用于产生相应对象” 的函数指针之间必须对应。针对特定的型别标识符,map 可以提供相应的函数取用机会,这正是 switch 语句提供的功能。map 还具有可伸缩性,这是编译器便被固定住的
switch 语句无法提供弹性。map 可与执行期增长,可动态增加元素(ID 和 函数指针的组合)。

为什么不使用vector?

ID 是整数,所以我们应该可以维护一个 vector,让 ID 称为 vector 的索引。那的却更快速,但这里使用 map更好一些,因为 map 并不要求其索引是连续值,而且 map 更具有一般性-----
vector 只能使用整数索引, maps 却可以拿任何有序型别作为索引。在泛型化 Object Factory 时,这一点很重要。

class Shape
{
public:
	virtual void Draw() const = 0;
	virtual void Rotate(double angle) = 0;
	virtual void Zoom(double zoomFactor) = 0;
};

class Line : public Shape
{
public:
	void Draw() const
	{

	}
	void Rotate(double angle)
	{

	}
	void Zoom(double zoomFactor)
	{

	}

};

class  ShapeFactory
{
public:
	typedef Shape* (*CreateShapeCallback)();
private:
	typedef std::map<int, CreateShapeCallback> CallbackMap;
public:
	// Return true if registration was successful
	bool RegisterShape(int ShapId, CreateShapeCallback CreateFn);
	// Return true if the ShapeId was registered before
	bool UnregisterShape(int ShapId);
	Shape* CreateShape(int ShapId);
private:
	CallbackMap callbacks_;
};

bool ShapeFactory::RegisterShape(int ShapId, CreateShapeCallback CreateFn)
{
	return callbacks_.insert(CallbackMap::value_type(ShapId, CreateFn)).second;
}
bool ShapeFactory::UnregisterShape(int ShapId)
{
	return callbacks_.erase(ShapId) == 1;
}
Shape* ShapeFactory::CreateShape(int ShapId)
{
	CallbackMap::const_iterator i = callbacks_.find(ShapId);
	if (i == callbacks_.end())
	{
		// not found
		throw std::runtime_error("Unknown Shape ID");
	}
	return (i->second)();
}

ShapeFactory ShapeFactory_;

namespace
{
	Shape* CreateLine()
	{
		return new Line;
	}
	// The ID of class Line
	const int LINE = 1;
	// Assume TheShapeFactory is a singleton factory
	const bool registered = ShapeFactory_.RegisterShape(LINE, CreateLine);

}

【上篇】
【下篇】

抱歉!评论已关闭.