C++设计模式12——原型(Prototype)模式
1. 原型(Prototype)模式介绍
定义:
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节而已。
如何使用:
要提供自己的拷贝构造函数进行深拷贝,方便用户,还应该提供一个clone
方法(实际就是去调用拷贝构造函数)
关键代码:
实现克隆clone
函数,返回值为类的指针,返回new 类(*this)
模式的结构:
原型模式包含以下主要角色。
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的
clone()
方法,它是可被复制的对象。 - 访问类:使用具体原型类中的
clone()
方法来复制新的对象。
类图:
2. 为了方便理解,举个例子
定义一个羊类,实现clone
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<string.h>
using namespace std;
class Sheep {
friend ostream& operator<<(ostream&, Sheep& sheep);
public:
//构造函数
Sheep(char* name,int age) {
this->age = age;
this->name = new char[strlen(name) + 1] { 0 };
strcpy(this->name, name);
}
//拷贝构造函数
Sheep(const Sheep& sheep) {
this->age = sheep.age;
this->name = new char[strlen(sheep.name) + 1] { 0 };
strcpy(this->name, sheep.name);
}
~Sheep() {
if (name != NULL) {
delete name;
}
}
//重载 = 赋值运算符
Sheep& operator=(const Sheep& sheep) {
//1、防止自身赋值
if (this == &sheep) {
return *this;
}
//2、释放原来在堆空间开辟的空间
if (this->name != NULL) {
delete this->name;
this->name = NULL;
}
//3、执行深拷贝
this->age = sheep.age;
this->name = new char[strlen(sheep.name) + 1] { 0 };
strcpy(this->name, sheep.name);
}
Sheep* clone() {
//直接调用 拷贝构造函数,因为拷贝构造函数已经做了深拷贝的动作。
return new Sheep(*this);
}
char* getName() {
return name;
}
void setName(char* name) {
if (this->name != NULL) {
delete this->name;
this->name = NULL;
}
this->name = new char[strlen(name) + 1] { 0 };
strcpy(this->name, name);
}
int getAge() {
return age;
}
void setAge(int age) {
this->age = age;
}
private:
char* name;
int age;
};
//重载<<运算符
ostream& operator<<(ostream&, Sheep& sheep) {
cout << "[name = " << sheep.name << " ,age = "<< sheep.age << "]";
return cout;
}
int main(int argc, char *argv[]) {
Sheep* sheep1 = new Sheep("多利",0);
Sheep* sheep2 = sheep1->clone();
cout << "sheep1:" << *sheep1 << endl;
cout << "sheep2:" << *sheep2 << endl;
cout << "删除克隆原型sheep1" << endl;
delete sheep1;
cout << "sheep2:" << *sheep2 << endl;
return EXIT_SUCCESS;
}
3. 原型(Prototype)模式优缺点
优点:
- 性能提高。
- 逃避构造函数的约束。
缺点:
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
- 必须实现 Cloneable 接口。