Prototype模式——设计模式学习
Prototype
一 意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二 动机
在图形编辑器中增加音符编辑构造乐谱编辑器的例子中。
GraphicTool作为图形编辑器的框架,其中提供了可以添加的图形;
Graphic作为图形类的基类,新增加的音乐相关符号也都从Graphic派生。
要实现乐谱构造器,需要新增加图形,供GraphicTool添加使用。
如果仅仅是增加新的Graphic派生类,并且再对每一个Graphic实现copy自己。我想了很久也不知道怎么去实现……
增加了新的Graphic派生类,但是GraphicTool仍然是不知道增加了新的图形,要么扩展GraphicTooL支持的图形种类,
要么增加GraphicTooL派生类扩展。不管什么样的做法,我觉得都需要知道我们新增加了Graphic。
所以我觉得使用Prototype模式在这里来实现图形扩展,而不改变客户端GraphicTool等代码,来支持新的特性,似乎有些不妥当。
Prototype模式——原型模式本身就要求先有原型然后才有Clone。
当然我觉得这里每一个Graphic类增加Clone是十分必要的。在图形编辑器中,GraphicTool所支持的可以添加的每一类图形,
都是可以批量添加的,如果每添加或者copy一个图形都需要new一次然后初始化,对于复杂的对象,使用new是非常麻烦的事。
只需要为每一类图形保留一个原型,然后Clone Copy就可以了。
三 适用性与结构
- 当一个系统应该独立于产品创建,构成和表示时,要使用Prototype模式;
- 当要实例化的类是在运行时刻指定时,例如:通过动态加载
- 为了避免创建一个与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同的状态组合中的一种时,建立相应数目的原型
并Clone他们,而不用去手工创建和初始化。
四 代码实现
1 基本形式
/***********************************************
* Class Frame *
**********************************************/
class Frame
{
public:
virtual void draw() = 0;
virtual Frame* Clone() = 0;
};
/***********************************************
* Class Title *
**********************************************/
class Title : public Frame
{
public:
Title(){}
Title(Title& other)
{
cout<<"copy title"<<endl;
}
virtual void draw()
{
cout<<"title draw"<<endl;
}
virtual Frame* Clone()
{
return new Title(*this);
}
};
/***********************************************
* Class Menu *
**********************************************/
class Menu : public Frame
{
public:
Menu(){}
Menu(Menu& other)
{
cout<<"copy menu"<<endl;
}
virtual void draw()
{
cout<<"menu draw"<<endl;
}
virtual Frame* Clone()
{
return new Menu(*this);
}
};
/***********************************************
* Class Toolbar *
**********************************************/
class Toolbar : public Frame
{
public:
Toolbar(){}
Toolbar(Toolbar& other)
{
cout<<"copy Toolbar"<<endl;
}
virtual void draw()
{
cout<<"Toolbar draw"<<endl;
}
virtual Frame* Clone()
{
return new Toolbar(*this);
}
};
2 使用原型管理器
//来自:http://sourcemaking.com/design_patterns/prototype/cpp/3
#include <iostream.h>
enum imageType
{
LSAT, SPOT
};
class Image
{
public:
virtual void draw() = 0;
static Image *findAndClone(imageType);
protected:
virtual imageType returnType() = 0;
virtual Image *clone() = 0;
// As each subclass of Image is declared, it registers its prototype
static void addPrototype(Image *image)
{
_prototypes[_nextSlot++] = image;
}
private:
// addPrototype() saves each registered prototype here
static Image *_prototypes[10];
static int _nextSlot;
};
Image *Image::_prototypes[];
int Image::_nextSlot;
// Client calls this public static member function when it needs an instance
// of an Image subclass
Image *Image::findAndClone(imageType type)
{
for (int i = 0; i < _nextSlot; i++)
if (_prototypes[i]->returnType() == type)
return _prototypes[i]->clone();
}
class LandSatImage: public Image
{
public:
imageType returnType()
{
return LSAT;
}
void draw()
{
cout << "LandSatImage::draw " << _id << endl;
}
// When clone() is called, call the one-argument ctor with a dummy arg
Image *clone()
{
return new LandSatImage(1);
}
protected:
// This is only called from clone()
LandSatImage(int dummy)
{
_id = _count++;
}
private:
// Mechanism for initializing an Image subclass - this causes the
// default ctor to be called, which registers the subclass's prototype
static LandSatImage _landSatImage;
// This is only called when the private static data member is inited
LandSatImage()
{
addPrototype(this);
}
// Nominal "state" per instance mechanism
int _id;
static int _count;
};
// Register the subclass's prototype
LandSatImage LandSatImage::_landSatImage;
// Initialize the "state" per instance mechanism
int LandSatImage::_count = 1;
class SpotImage: public Image
{
public:
imageType returnType()
{
return SPOT;
}
void draw()
{
cout << "SpotImage::draw " << _id << endl;
}
Image *clone()
{
return new SpotImage(1);
}
protected:
SpotImage(int dummy)
{
_id = _count++;
}
private:
SpotImage()
{
addPrototype(this);
}
static SpotImage _spotImage;
int _id;
static int _count;
};
SpotImage SpotImage::_spotImage;
int SpotImage::_count = 1;
// Simulated stream of creation requests
const int NUM_IMAGES = 8;
imageType input[NUM_IMAGES] =
{
LSAT, LSAT, LSAT, SPOT, LSAT, SPOT, SPOT, LSAT
};
int main()
{
Image *images[NUM_IMAGES];
// Given an image type, find the right prototype, and return a clone
for (int i = 0; i < NUM_IMAGES; i++)
images[i] = Image::findAndClone(input[i]);
// Demonstrate that correct image objects have been cloned
for (i = 0; i < NUM_IMAGES; i++)
images[i]->draw();
// Free the dynamic memory
for (i = 0; i < NUM_IMAGES; i++)
delete images[i];
}
五 总结
Prototype模式在工作的平台使用较少。
Prototype模式例子的实现中,都是只针对浅拷贝,如果具有对象的循环应用或者动态内存成员,那情况可能变得更加的复杂一些。