定义:
利用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性:
用原型模式,就是为了创建对象。适合原型模式的最好选择如下:
1.当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些;
2.有的时候,我们需要一个对象在某个状态下的副本,此时,我们使用原型模式是最好的选择;例如:一个对象,经过一段处理之后,其内部的状态发生了变化;这个时候,我们需要一个这个状态的副本,如果直接new一个新的对象的话,但是它的状态是不对的,此时,可以使用原型模式,将原来的对象拷贝一个出来,这个对象就和之前的对象是完全一致的了;
3.当我们处理一些比较简单的对象时,并且对象之间的区别很小,可能就几个属性不同而已,那么就可以使用原型模式来完成,省去了创建对象时的麻烦了;
4.有的时候,创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。
->适当的时候考虑一下原型模式,能减少对应的工作量,减少程序的复杂度,提高效率。
当一个系统应该独立于他的产品创建、构成和表示时,需要使用原型模式
当要实例化的类是在运行时刻指定时,如通过动态装载
为了避免创建一个与产品类层次平行的工厂类层次时
当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便一些。
复制一个对象分为浅拷贝和深拷贝:
浅拷贝:就是给对象中的每个成员变量进行赋值,就是把A1类中的变量直接赋给A2类中变量,属于值传递,但是涉及到有new之类内存分配的地方,他们却是共享内存的。
深拷贝:就是不仅使用值传递,而是要每个变量都有自己一份独立的内存空间,互不干扰。
默认的拷贝构造函数是浅拷贝的,如果要实现深拷贝,就需要重写拷贝构造函数T(const T&)。
UML图:
由于克隆需要一个原型,而上面的类图中Prototype就是这个原型,Prototype定义了克隆自身的Clone接口,由派生类进行实现,而实现原型模式的重点就在于这个Clone接口的实现。ConcretePrototype1类和ConcretePrototype2类继承自Prototype类,并实现Clone接口,实现克隆自身的操作;同时,在ConcretePrototype1类和ConcretePrototype2类中需要重写默认的复制构造函数,供Clone函数调用,Clone就是通过在内部调用重写的复制构造函数实现的。
#include <iostream>
#include <string>
using namespace std;
//原型类(抽象类)
class Prototype
{
private:
string str;
public:
Prototype(string s):str(s){}
Prototype():str(""){}
void show()
{
cout << str << endl;
}
virtual Prototype *clone() = 0;
};
//具体原型类
class ConcretePrototype1 :public Prototype
{
public:
ConcretePrototype1(string s) :Prototype(s){}
ConcretePrototype1(){}
virtual Prototype *clone()
{
ConcretePrototype1 *p = new ConcretePrototype1();
*p = *this;
return p;
}
};
class ConcretePrototype2 :public Prototype
{
public:
ConcretePrototype2(string s) :Prototype(s){}
ConcretePrototype2(){}
virtual Prototype *clone()
{
ConcretePrototype2 *p = new ConcretePrototype2();
*p = *this;
return p;
}
};
int main()
{
ConcretePrototype1 *test1 = new ConcretePrototype1("小菜");
ConcretePrototype2 *test2 = (ConcretePrototype2 *)test1->clone();
test1->show();
test2->show();
return 0;
}
输出:
小菜
小菜
#include <iostream>
#include <string>
using namespace std;
class Resume
{
private:
string name,sex,age,timeArea,company;
public:
Resume(string s):name(s){}
void setPersonalInfo(string s,string a)
{
sex=s;
age=a;
}
void setWorkExperience(string t,string c)
{
timeArea=t;
company=c;
}
void display()
{
cout<<name<<" "<<sex<<" "<<age<<endl;
cout<<"工作经历: "<<timeArea<<" "<<company<<endl<<endl;
}
Resume *clone()
{
// Resume *b = new Resume(name);
// b->setPersonalInfo(sex,age);
// b->setWorkExperience(timeArea,company);
// return b;
return new Resume(*this);//调用拷贝构造函数
}
};
int main()
{
Resume *r=new Resume("李俊宏");
r->setPersonalInfo("男","26");
r->setWorkExperience("2007-2010","读研究生");
r->display();
Resume *r2=r->clone();
r2->setWorkExperience("2003-2007","读本科");
r2->display();
return 0;
}
上面代码的数据成员都是值类型,拷贝时会逐位赋值。但是当字段是引用/指针类型,赋值引用/指针,但不赋值其指向的对象,即原始对象和其副本是同一对象。如下:
#include <iostream>
#include <string>
using namespace std;
class WorkExperirnce
{
private:
public:
string workDate;
string company;
WorkExperirnce(string d,string c):workDate(d),company(c){}
};
class Resume
{
private:
string name,sex,age;
WorkExperirnce *work;
public:
Resume(string s):name(s)
{
work=new WorkExperirnce("","");
}
void setPersonalInfo(string s,string a)
{
sex=s;
age=a;
}
void setWorkExperience(string d,string e)
{
work->workDate=d;
work->company=e;
}
void display()
{
cout<<name<<" "<<sex<<" "<<age<<endl;
cout<<"工作经历: "<<work->workDate<<" "<<work->company<<endl<<endl;
}
Resume *clone()
{
// Resume *b = new Resume(name);
// b->setPersonalInfo(sex,age);
// b->setWorkExperience(timeArea,company);
// return b;
return new Resume(*this);//调用拷贝构造函数
}
};
int main()
{
Resume *r=new Resume("李俊宏");
r->setPersonalInfo("男","26");
r->setWorkExperience("2007-2010","读研究生");
r->display();
Resume *r2=r->clone();//浅拷贝
r2->setWorkExperience("2003-2007","读本科");
r2->display();
r->display();
return 0;
}
重新设置r2的工作经验后,r的也改变了。r和r2指向的其实是同一对象
深拷贝实现:
#include <iostream>
#include <string>
using namespace std;
class WorkExperirnce
{
private:
public:
string workDate;
string company;
WorkExperirnce(string d,string c):workDate(d),company(c){}
WorkExperirnce(){}
WorkExperirnce* clone()
{
return new WorkExperirnce();
}
};
class Resume
{
private:
string name,sex,age;
WorkExperirnce *work;
Resume(WorkExperirnce *work1)
{
work=work1->clone();
}
public:
Resume(string s):name(s)
{
work=new WorkExperirnce();
}
void setPersonalInfo(string s,string a)
{
sex=s;
age=a;
}
void setWorkExperience(string d,string e)
{
work->workDate=d;
work->company=e;
}
void display()
{
cout<<name<<" "<<sex<<" "<<age<<endl;
cout<<"工作经历: "<<work->workDate<<" "<<work->company<<endl<<endl;
}
Resume *clone()
{
//调用私有的构造方法,让“工作经历”克隆完成,然后再给简历的其他字段赋值,最终返回深拷贝的简历对象
Resume *obj=new Resume(this->work);
obj->name=this->name;
obj->age=this->age;
obj->sex=this->sex;
return obj;
}
};
int main()
{
Resume *r=new Resume("李俊宏");
r->setPersonalInfo("男","26");
r->setWorkExperience("2007-2010","读研究生");
r->display();
Resume *r2=r->clone();//深拷贝
r2->setWorkExperience("2003-2007","读本科");
r2->display();
r->display();//r所指对象没有改变
return 0;
}
