原型模式的定义
原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制现有对象来创建新对象,而不是通过实例化类来创建新的对象。这种模式特别适用于需要大量类似对象的场景,可以提高系统的性能,降低创建对象的复杂性。
核心思想
原型模式的核心思想是:通过给出一个原型对象,并通过调用该原型对象的clone方法来创建一个新的对象。这个新对象是原型对象的一个克隆(浅复制或深复制),并且可以独立于原型对象进行修改。
原型模式的组成
-
原型接口(Prototype Interface) :定义一个
clone方法,用于克隆对象。 -
具体原型类(Concrete Prototype Class) :实现原型接口,具体实现
clone方法,返回对象的副本。 -
客户端(Client) :使用原型实例创建新对象。
优点
-
减少子类的创建:通过克隆现有对象,可以避免创建大量子类。
-
提高性能:克隆对象比通过实例化类创建新对象更高效,尤其是在创建复杂对象时。
-
简化对象的创建过程:通过复制已有对象,可以简化复杂对象的创建过程。
缺点
-
深拷贝和浅拷贝问题:在实现深拷贝时,需要特别注意对象中引用类型的数据的复制问题。
-
克隆方法的实现复杂度:对于包含复杂引用关系的对象,实现克隆方法可能会比较复杂。
代码示例
1、工作经历类
这个类包含了简历中的工作经历信息。包含公司名称、职位和工作时长。提供了一个拷贝构造函数用于深拷贝。
// 工作经历类
class WorkExperience
{
public:
WorkExperience(const std::string& company, const std::string& position, const std::string duration)
: company(company), position(position), duration(duration) {}
// 拷贝构造函数
WorkExperience(const WorkExperience& other)
: company(other.company), position(other.position), duration(other.duration) {}
void print() const
{
std::cout << "Company: " << company << ", Position: " << position << ", Duration: " << duration << std::endl;
}
private:
std::string company;
std::string position;
std::string duration;
};
2、原型接口
这个类定义了一个原型接口,包含克隆方法和打印方法。
// 原型接口
class Prototype
{
public:
virtual ~Prototype() = default;
virtual std::unique_ptr<Prototype> clone() const = 0;
virtual void print() const = 0;
};
3、简历类
简历类继承自原型接口,实现了克隆方法和打印方法。
// 简历类
class Resume :public Prototype
{
private:
std::string name;
int age;
std::vector<WorkExperience> workExperiences;
public:
Resume(const std::string& name, int age) :name(name), age(age) {}
void addWorkExperience(const std::string& company, const std::string& position, const std::string& duration)
{
workExperiences.emplace_back(company, position, duration);
}
std::unique_ptr<Prototype> clone() const override
{
// 深拷贝,创建一个新Resume对象,并复制所有成员变量
std::unique_ptr<Resume> newResume = std::make_unique<Resume>(name, age);
// 这里隐含了WorkExperience的拷贝构造函数调用
newResume->workExperiences = workExperiences;
return newResume;
}
void setName(const std::string& newName)
{
name = newName;
}
void setAge(int newAge)
{
age = newAge;
}
void print() const override
{
std::cout << "Name: " << name << ", Age: " << age << std::endl;
for (const auto& we : workExperiences)
{
we.print();
}
}
};
4、客户端代码
在客户端代码中,我们创建了一个简历对象,并添加了一些工作经历。然后克隆这个简历对象,并修改克隆对象的部分信息,最后打印出原型简历和克隆简历的信息。
// 客户端代码
int main()
{
// 创建原型简历对象
std::unique_ptr<Resume> originalResume = std::make_unique<Resume>("小明", 25);
originalResume->addWorkExperience("Company A", "Developer", "2010-2015");
originalResume->addWorkExperience("Company B", "Senior Developer", "2015-2020");
// 克隆简历对象
std::unique_ptr<Resume> clonedResume = std::unique_ptr<Resume>(static_cast<Resume*>(originalResume->clone().release()));
// 修改克隆对象的信息
clonedResume->setName("小红");
clonedResume->setAge(28);
clonedResume->addWorkExperience("Company C", "Lead Developer", "2020-Present");
// 打印原型简历和克隆简历的信息
std::cout << "Original Resume:" << std::endl;
originalResume->print();
std::cout << "\nCloned Resume:" << std::endl;
clonedResume->print();
system("pause");
return 0;
}
std::unique_ptr<Resume> clonedResume = std::unique_ptr<Resume>(static_cast<Resume*>(originalResume->clone().release()));所做的工作:
- 调用
clone方法:
originalResume->clone(),
这会调用 Resume 类的 clone 方法,返回一个 std::unique_ptr<Prototype> 对象,其中包含一个 Resume 对象的副本。
- 调用
release方法:
originalResume->clone().release(),release 方法会放弃 std::unique_ptr 对所管理对象的所有权,并返回一个原始指针(Prototype*)。这意味着返回的指针指向了之前由 std::unique_ptr 管理的对象,但该对象现在需要手动管理其生命周期。
- 类型转换:
static_cast<Resume*>(originalResume->clone().release()),使用 static_cast 将 Prototype* 强制转换为 Resume*。这在逻辑上是安全的,因为我们知道 clone 方法返回的实际对象类型是 Resume。
- 创建新的
std::unique_ptr:
std::unique_ptr<Resume>(static_cast<Resume*>(originalResume->clone().release())),创建一个新的 std::unique_ptr<Resume>,并将之前的原始指针(Resume*)交给它管理。这确保了新对象在离开作用域时会被自动释放,避免内存泄漏。