原型模式:属于对象创建型模式的一种,用于根据原型实例创建指定类型的对象,并且通过拷贝这些原型来创建新的对象。
适用性:
当一个系统需要独立于它的产品创建、构成和表示时,要使用ProtoType模式;以及当要实例化的类是在运行时指定,如通过动态加载或者为了避免创建一个与产品层次平行的工厂类层次时;或者当一个类的示例只能有几个不同的状态组合中的一组时建立相应的原型克隆核能比每次用合适的状态手工示例话方便些。
如上是通用型原型模式的UML类图,主要由一个抽象的原型类提供一个克隆自身的接口clone,具体的原型类继承该类并实现clone接口(通过调用类的拷贝构造函数实现克隆)
实现的效果:
1、可以再运行时动态的添加和删除产品
2、改变值以指定新对象
3、改变结构以创建新对象
4、减少子类的构造
下面贴出通用型原型模式的代码
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.c++
#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";
}
客户端代码:
#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(){}
客户端代码
#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的建立,由此看来原型模式的确可以减少类或对象的个数。