一、原型(Prototype)模式概述
DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝(既clone)。可以说,拷贝(clone)是原型模式的精髓所在。其中拷贝分为深拷贝和浅拷贝。浅复制:被复制的对象的所有变量都含有与原来的对象相同的值,而所有对其他对象的引用都仍然指向原来的对象。深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。Clone的时候使用深复制。
创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,而且也不需要知道任何创建的细节。这无疑是一种非常有效的方式,快速的创建一个新的对象。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,有对性能是大大的提高。因为如果不用Clone,每次new,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就实在是太低效了。
二、原型模式的优点和缺点及常见可用场景
1、原型模式的优点
- 性能优良,原型模式是在内存二进制流的拷贝,当new的对象比较复杂的时候,要比直接new一个对象性能好很多,特别是在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点。
- 原型模式允许在运行时动态改变具体的实现类型。原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
- 对客户隐藏制造新实例的复杂性
2、原型模式的缺点
- 由于是直接在内存中拷贝,构造函数不会执行。
- 原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候,耦合度较高。
3、适合使用原型模式的场景及注意事项
- 当通过初始化对象时需要消耗非常多的资源的时候,包括软硬件资源
- 当通过new 一个对象需要准备繁琐的数据或复杂的权限时
- 当一个对象需要提供给其他对象访问的时候,而且各个调用者可能会改变其属性的时候可以考虑使用原型模式构造多个副本对象供其他对象调用,又称为保护性拷贝。
- 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。即final修饰的不会被拷贝。
例子:找工作时,需要准备简历。假如没有打印设备,需手写简历,其简历内容都是完全相同的内容。这样有个缺陷,如果要修改简历中的某项内容(比如对于通信公司的简历想突出强调开发过4G xxx项目,5G xxx项目,对于C++要求高的公司想强调“精通”STL和设计模式。。。),那么所有已写好的简历都要修改,工作量很大。随着科技的进步,出现了打印设备。我们只需手写一份,然后利用打印设备复印多份即可。如果要修改简历中的某项,那么修改原始的版本就可以了,然后再复印。原始的那份手写稿相当于是一个原型,有了它,就可以通过复印(拷贝)创造出更多的新简历。这就是原型模式的基本思想。下面给出原型模式的UML图,以刚才那个例子为实例。
4、ULM图
#include <iostream>
#include <string>
#include <cstddef>
#include <memory>
class Resume
{
protected:
std::string name;
std::string skill;
//年龄,工作年限,项目等省略...
public:
Resume() = default;
Resume(const std::string& name, const std::string& skill)
: name{name}
, skill{skill}
{
}
virtual ~Resume() = default;
virtual void setName(const std::string&) = 0;
virtual void setSkill(const std::string&) = 0;
virtual void show() = 0;
virtual std::unique_ptr<Resume> clone() = 0;
};
class ResumeA : public Resume
{
public:
ResumeA(const std::string& name, const std::string& skill) :Resume{name, skill}
{
this->name = name;
this->skill = skill;
}
ResumeA(const ResumeA& rhs)
{
this->name = rhs.name;
this->skill = rhs.skill;
}
~ResumeA() = default;
void setName(const std::string& name)
{
this->name = name;
}
void setSkill(const std::string& skill)
{
this->skill = skill;
}
void show()
{
std::cout << "ResumeA name : " << name << ", skill: " << skill << std::endl;
}
std::unique_ptr<Resume> clone() //克隆,关键所在
{
return std::make_unique<ResumeA>(*this);
}
};
int main()
{
auto r1 = new ResumeA("PersionA", "精通C,熟悉C++...");
auto r3 = r1->clone();
r1->show();
//想去面试C++要求高的公司
r3->setSkill("精通C++ STL, design pattern...");
r3->show();
return 0;
}
结果如下:
另外我们也可以把工作经验放到一个单独的类中,摘自:https://blog.csdn.net/xiqingnian/article/details/41983337
Prototype.h
#ifndef PROTOTYPE_H
#define PROTOTYPE_H
#include <iostream>
#include <string>
//工作经验类:WorkExperience类
class WorkExperience
{
private:
std::string workDate;
std::string company;
public:
WorkExperience(){}
~WorkExperience(){}
//供克隆调用的构造函数
WorkExperience(WorkExperience* work)
{
this->workDate=work->workDate;
this->company=work->company;
}
std::string getWorkDate()
{
return workDate;
}
void setWorkDate(std::string workDate)
{
this->workDate=workDate;
}
std::string getCompany()
{
return company;
}
void setCompany(std::string company)
{
this->company=company;
}
//克隆
WorkExperience* Clone()
{
//调用供克隆调用的构造函数WorkExperience(WorkExperience)
return new WorkExperience(this);
}
};
//原型类,声明一个克隆自身的接口。
class Prototype
{
protected:
std::string name;
std::string sex;
std::string age;
std::string timeArea;
std::string company;
public:
virtual void SetPersonalInfo(std::string sex,std::string age)=0;
virtual void Display()=0;
virtual Prototype* Clone()=0;
};
//具体原型类,即ConcretePrototype类,本例中为简历Resume类,实现一个克隆自身的操作。
class Resume:public Prototype
{
private:
WorkExperience* work;
public:
Resume(std::string name)
{
this->name=name;
work=new WorkExperience();
}
~Resume()
{
if(work!=NULL)
delete work;
}
//供克隆调用的构造函数
Resume(WorkExperience* work)
{
this->work=work->Clone();
}
void SetPersonalInfo(std::string sex,std::string age)
{
this->sex=sex;
this->age=age;
}
void SetWorkExperience(std::string workDate,std::string company)
{
work->setWorkDate(workDate);
work->setCompany(company);
}
void Display()
{
std::cout<<name<<" "<<sex<<" "<<age<<std::endl;
std::cout<<"工作经历 "<<work->getWorkDate()<<" "<<work->getCompany()<<std::endl;
}
//克隆
Resume* Clone()
{
//调用 供克隆函数调用的构造函数Resume(work)
Resume *cloneResume=new Resume(work);
cloneResume->name=this->name;
cloneResume->age=this->age;
cloneResume->sex=this->sex;
return cloneResume;
}
};
#endif
main.cpp
#include "Prototype.h"
#include <iostream>
#include <stdlib.h>
void main()
{
Resume* a=new Resume("大鸟");
a->SetPersonalInfo("男","29");
a->SetWorkExperience("1998-2000","XX公司");
Resume* b=a->Clone();
b->SetWorkExperience("1998-2006","YY公司");
Resume* c=b->Clone();
c->SetPersonalInfo("男","24");
c->SetWorkExperience("1998-2003","ZZ公司");
a->Display();
b->Display();
c->Display();
delete a;
delete b;
delete c;
a=b=c=NULL;
system("pause");
}