原型模式(Prototype Pattern)
定义[^1]
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
通过仿照C#中的Cloneable()接口进行实现[^2]
ICloneable.h
class ICloneable
{
public:
virtual ~ICloneable() {}
virtual ICloneable* clone() const = 0;
};
Resume.h
#pragma once
#include "ICloneable.h"
#include <string>
class Resume :
public ICloneable
{
public:
Resume();
Resume(const std::string& name);
virtual ~Resume();
void setPersonalInfo(const std::string& sex, const std::string& age);
void setWorkExperience(const std::string& company, const std::string& timeArea);
void display() const;
virtual ICloneable* clone() const override;
std::string getName() const;
std::string getSex() const;
std::string getAge() const;
std::string getCompany(const std::string& timeArea) const;
std::string getTimeArea(const std::string& company) const;
private:
std::string mName = std::string("");
std::string mSex = std::string("");
std::string mAge = std::string("");
std::string mTimeArea = std::string("");
std::string mCompany = std::string("");
};
Resume.cpp
#include "Resume.h"
#include <iostream>
Resume::Resume()
{
}
Resume::Resume(const std::string& name) : mName(name)
{
}
Resume::~Resume()
{
}
void Resume::setPersonalInfo(const std::string& sex, const std::string& age)
{
mSex = sex;
mAge = age;
}
void Resume::setWorkExperience(const std::string& company, const std::string& timeArea)
{
mCompany = company;
mTimeArea = timeArea;
}
void Resume::display() const
{
std::cout << getName() << " " << getSex() << " " << getAge() << std::endl;
std::cout << "工作经历:" << getCompany(mTimeArea) << " " << getTimeArea(mCompany) << std::endl;
}
ICloneable* Resume::clone() const
{
return static_cast<ICloneable *>(new Resume(*this));
}
std::string Resume::getName() const
{
return mName;
}
std::string Resume::getSex() const
{
return mSex;
}
std::string Resume::getAge() const
{
return mAge;
}
std::string Resume::getCompany(const std::string& timeArea) const
{
if (timeArea == mTimeArea)
return mCompany;
else
return std::string("");
}
std::string Resume::getTimeArea(const std::string& company) const
{
if (company == mCompany)
return mTimeArea;
else
return std::string("");
}
其实这里,并不能看出什么问题;
我们只知道覆写clone()中有 new Resume(*this) 这段代码,它对应了复制构造函数的标准固定形式 Resume(const Resume&) ;
但是,我们并没有发现这个构造函数的声明和定义,它目前是由编译器默认生成的(同时,还包括赋值运算符…)。
接下来,我们新增一个 WorkExperience 类
WorkExperience.h
#pragma once
#include <string>
class WorkExperience
{
public:
void setCompany(const std::string& company);
std::string getCompany() const;
void setWorkData(const std::string& workData);
std::string getWorkData() const;
private:
std::string mWorkData = std::string("");
std::string mCompany = std::string("");
};
对 Resume 类中的两个成员变量做一个替换
#include "WorkExperience.h"
class ResumeS :
public ICloneable
{
public:
ResumeS();
ResumeS(const std::string& name);
virtual ~ResumeS();
virtual ICloneable* clone() const override;
private:
std::string mName = std::string("");
std::string mSex = std::string("");
std::string mAge = std::string("");
WorkExperience* mWorkExp = nullptr;
};
此时,你如果运行下面的代码,将会让你眼前一亮
Resume* rs1 = new Resume(std::string("码农A"));
rs1->setPersonalInfo(std::string("男"), std::string("31"));
rs1->setWorkExperience(std::string("XX公司"), std::string("1998-2000"));
Resume* rs2 = dynamic_cast<Resume*>(rs1->clone());
rs2->setWorkExperience(std::string("YY公司"), std::string("1998-2006"));
Resume* rs3 = dynamic_cast<Resume*>(rs1->clone());
rs3->setWorkExperience(std::string("ZZ公司"), std::string("1998-2003"));
rs1->display();
rs2->display();
rs3->display();
ResumeS* rss1 = new ResumeS(std::string("码农A"));
rss1->setPersonalInfo(std::string("男"), std::string("31"));
rss1->setWorkExperience(std::string("XX公司"), std::string("1998-2000"));
ResumeS* rss2 = dynamic_cast<ResumeS*>(rss1->clone());
rss2->setWorkExperience(std::string("YY公司"), std::string("1998-2006"));
ResumeS* rss3 = dynamic_cast<ResumeS*>(rss1->clone());
rss3->setWorkExperience(std::string("ZZ公司"), std::string("1998-2003"));
rss1->display();
rss2->display();
rss3->display();
码农A在不同的时间段有三段不同的工作经历,而码农B却只有三份一模一样的工作经历。
这里,需要抛出两个概念:浅拷贝 vs. 深拷贝[^3]
C++编译器默认生成的复制构造函数只进行浅拷贝,即只拷贝成员变量的值。
*那这个时候,该怎么办呢? *
一、让WorkExperience继承抽象基类ICloneable
#include "ICloneable.h"
class WorkExperienceT : public ICloneable
{
public:
virtual ~WorkExperienceT();
virtual ICloneable* clone() const override;
};
ICloneable* WorkExperienceT::clone() const
{
return static_cast<ICloneable*>(new WorkExperienceT(*this));
}
同时,在Resume类中新增一个私有的构造函数
class ResumeT :
public ICloneable
{
private:
ResumeT(const std::string& name, WorkExperienceT& we);
}
ResumeT::ResumeT(const std::string& name, WorkExperienceT& we) : mName(name)
{
mWorkExp = dynamic_cast<WorkExperienceT*>(we.clone());
}
ICloneable* ResumeT::clone() const
{
ResumeT* newRst = new ResumeT(getName(), *mWorkExp);
newRst->setPersonalInfo(getSex(), getAge());
return static_cast<ICloneable*>(newRst);
}
二、自定义复制构造函数
ResumeT(const ResumeT& rst);
ResumeT::ResumeT(const ResumeT& rst)
{
if (this == (&rst))
return;
if (!(this->mWorkExp))
{
this->mWorkExp = new WorkExperienceT();
}
this->mWorkExp->setCompany(rst.getCompany());
this->mWorkExp->setWorkData(rst.getTimeArea());
this->mName = rst.getName();
this->mSex = rst.getSex();
this->mAge = rst.getAge();
}
ICloneable* ResumeT::clone() const
{
return static_cast<ICloneable*>(new ResumeT(*this));
}
关于自定义构造函数可能还不完全,还需要自定义赋值运算符…
关于扩展,还可以用智能指针unique_ptr/make_unique的方式,仿照C#的Cloneable()接口来实现原型模式…
另外,也可以从多线程的数据竞争的问题角度来考量实现原型模式…
参考资料
[^1]: https://book.douban.com/subject/25843319/
[^2]: https://book.douban.com/subject/2334288/
[^3]:https://blog.csdn.net/m0_59068776/article/details/133916375