实现要点:
1.使用原型管理器,体现在一个系统中原型数目不固定时,可以动态的创建和销毁。
2.实现克隆操作,在.NET中可以使用Object类的MemberwiseClone()方法来实现对象的浅表拷贝或通过序列化的方式来实现深拷贝,在C++中就是拷贝构造函数的作用。
3.Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口。
效果:
1.它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。
2. Prototype模式允许客户只通过注册原型实例就可以将一个具体产品类并入到系统中,客户可以在运行时刻建立和删除原型。
3.减少了子类构造,Prototype模式是克隆一个原型而不是请求工厂方法创建一个,所以它不需要一个与具体产品类平行的Creater类层次。
4.Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。
5.产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构
6.Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
适用性:
- 当一个系统应该独立于他的产品创建、构成和表示时,需要使用原型模式
- 当要实例化的类是在运行时刻指定时,如通过动态装载
- 为了避免创建一个与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便一些。
通用型原型模式
ProtoType.h
#ifndef PROTOTYPE_H_
#define PROTOTYPE_H_
class ProtoType
{
public:
virtual ~ProtoType(){};
virtual ProtoType* clone() = 0;
virtual void show(){};
};
class ConcreteProtoType1:ProtoType
{
public:
virtual ProtoType* clone();
virtual void show();
};
class ConcreteProtoType2 :ProtoType
{
public:
virtual ProtoType* clone();
virtual void show();
};
#endif
PrptoType.cpp
#include "ProtoType.h"
#include <iostream>
ProtoType *ConcreteProtoType1::clone()
{
return new ConcreteProtoType1(*this);
}
void ConcreteProtoType1::show()
{
std::cout << "This is ConcreteProtoType1\n";
}
ProtoType* ConcreteProtoType2::clone()
{
return new ConcreteProtoType2(*this);
}
void ConcreteProtoType2::show()
{
std::cout << "This is ConcreteProtoType2\n";
}
main.cpp
#include "ProtoType.h"
int main()
{
ConcreteProtoType1 *proto1 = new ConcreteProtoType1();
ConcreteProtoType1 *proto11 = (ConcreteProtoType1*)proto1->clone();
proto1->show();
proto11->show();
ConcreteProtoType2* proto2 = new ConcreteProtoType2();
ConcreteProtoType2 *proto22 = (ConcreteProtoType2*)proto2->clone();
proto2->show();
proto22->show();
delete proto1;
delete proto11;
delete proto2;
delete proto22;
return 0;
}
需要注意的是,原型模式是通过拷贝构造函数来实现对指定类型对象的拷贝,而涉及到拷贝构造函数必然涉及到深复制和浅复制的问题,因此对于类包含了一个指向其他类型对象的指针时需要考虑深复制还是浅复制(具体可参考String类的拷贝构造函数)
一个设计到深复制的具体的原型模式的例子
Resume.h
#include <iostream>
class Resume
{
public:
Resume(char*);
Resume(const Resume &);
virtual ~Resume();
virtual Resume * clone() = 0;
protected:
char* m_name;
};
class ResumeA :public Resume
{
public:
ResumeA(char*);
ResumeA(const ResumeA&);
virtual ~ResumeA();
virtual ResumeA* clone(); //克隆,关键所在
void setName(char *);
};
class ResumeB :public Resume
{
public:
ResumeB(char*);
ResumeB(const ResumeB&);
virtual ~ResumeB();
virtual ResumeB* clone();
};
Resume.cpp
#include "resume.h"
Resume::Resume(char* str)
{
if (NULL == str)
{
m_name = new char[1];
m_name = '\0';
}
else
{
int len = strlen(str);
m_name = new char[len + 1];
strcpy_s(m_name, len + 1, str);
}
}
Resume::Resume(const Resume& resu)
{
int len = strlen(resu.m_name);
m_name = new char[len + 1];
strcpy_s(m_name, len + 1, resu.m_name);
}
Resume::~Resume()
{
delete m_name;
m_name = NULL;
}
ResumeA::ResumeA(char*str) :Resume(str)
{}
ResumeA::ResumeA(const ResumeA& other) : Resume(other)
{
}
void ResumeA::setName(char *str)
{
delete m_name;
m_name = new char[strlen(str) + 1];
strcpy_s(m_name, strlen(str) + 1, str);
}
ResumeA * ResumeA::clone()
{
std::cout << this->m_name << std::endl;
return new ResumeA(*this);
}
ResumeA::~ResumeA(){}
ResumeB::ResumeB(char*str) :Resume(str)
{}
ResumeB::ResumeB(const ResumeB& other) : Resume(other)
{
}
ResumeB * ResumeB::clone()
{
std::cout << m_name << std::endl;
return new ResumeB(*this);
}
ResumeB::~ResumeB(){}
main.cpp
#include "ProtoType.h"
#include "resume.h"
int main()
{
/*
ConcreteProtoType1 *proto1 = new ConcreteProtoType1();
ConcreteProtoType1 *proto11 = (ConcreteProtoType1*)proto1->clone();
proto1->show();
proto11->show();
ConcreteProtoType2* proto2 = new ConcreteProtoType2();
ConcreteProtoType2 *proto22 = (ConcreteProtoType2*)proto2->clone();
proto2->show();
proto22->show();
delete proto1;
delete proto11;
delete proto2;
delete proto22;
*/
ResumeA* resuA = new ResumeA("xiaoming");
ResumeA* resuA1 = resuA->clone();
ResumeA* resuA2 = resuA1->clone();
resuA2->setName("xiaohua");
ResumeA * resu3 = resuA2->clone();
ResumeA* resu4 = resu3->clone();
delete resuA;
delete resuA1;
delete resuA2;
delete resu3;
delete resu4;
return 0;
}
在上述具体的原型模式的例子中,由于类包含了一个简历的个人名称m_name成员,它是一个指向char*类型的指针,在通过克隆方法创建一个新的对象时必须为这个对象分配新的内存而不能让这个对象的m_name与原型执行同一个内存地址,否则的话一旦原型对象被释放了,导致后生成的对象的m_name也被释放了,会引起系统异常,因此需要实现自己的拷贝构造函数。
此外上述例子中创建了名为xiaoming的建立,现在如果需要创建xiaohua的简历仅通过类提供的一个setName方法改变原型对象的名称就可以用该对象创建xiaohua的建立,而通常的做法是需要新建一个xiaohua的建立类或者对象,通过该新建的对象为原型来创建xiaohua的建立,由此看来原型模式的确可以减少类或对象的个数。