下面我将详细讲解 C++ 设计模式之原型模式的完整攻略。
什么是原型模式?
原型模式是一种创建者模式,它通过复制现有对象来创建新对象,而不是直接实例化新对象。它通过在运行时动态生成对象副本的方式来创建新对象,避免了创建新对象的成本,并提高了性能。
原型模式的优缺点
优点:
- 在运行时生成新对象,避免了创建新对象的成本。
- 代码复杂度低,易于实现。
- 可以实现对象动态配置。
缺点:
- 所有的克隆方法都必须在导出类中实现。
- 克隆方法是递归的,需要额外的递归调用开销。
- 深克隆可能需要额外的编写代码和测试。
原型模式的实现方式
原型模式的实现方式可以采用两种方式来实现:浅拷贝和深拷贝。
浅拷贝:将一个对象复制到另一个对象中,新对象中的基本元素是源对象中相应元素的副本。这种方法比较简单,但如果源对象包含指向其他对象的指针,新对象中的指针将指向源对象中的相应指针所指向的地址,这种方法可能会引起问题。
深拷贝:将源对象的所有元素递归地复制到新对象中,使二者完全独立。这种方式比较安全,但开销比较大。
原型模式的实现步骤
- 定义一个抽象类或接口。
class ICloneable {
public:
virtual ICloneable* Clone() = 0;
};
- 定义一个需要被克隆的对象。
class Person : public ICloneable {
public:
string name;
int age;
Date birthday;
vector<Address*> addresses;
virtual ICloneable* Clone() {
// 深拷贝实现
Person* p = new Person(*this);
// 指针地址修改为新对象的地址
for (size_t i = 0; i < p->addresses.size(); i++) {
Address* a = p->addresses[i];
a = new Address(*a);
p->addresses[i] = a;
}
return p;
}
};
- 在需要克隆对象的位置进行调用,调用
Clone()
方法生成新对象。
int main() {
Person* p1 = new Person();
p1->name = "Tom";
p1->age = 25;
p1->birthday.year = 1996;
p1->birthday.month = 7;
p1->birthday.day = 5;
p1->addresses.push_back(new Address("China", "Beijing"));
p1->addresses.push_back(new Address("USA", "New York"));
Person* p2 = (Person*)p1->Clone();
// 修改新对象的值
p2->name = "Jerry";
p2->age = 30;
p2->birthday.year = 1991;
p2->birthday.month = 1;
p2->birthday.day = 1;
p2->addresses[0]->city = "Shanghai";
p2->addresses[1]->city = "Los Angeles";
return 0;
}
这样,在 main()
函数中,我们通过将原始对象 p1
传递到 Clone()
方法中生成新的对象 p2
,并修改新对象的值。如果进行的是浅拷贝的实现方式,新对象 p2
将会和原始对象 p1
共享同一个地址空间,修改 p2
的值将会对 p1
也造成影响。而深拷贝由于同字符串常量池的使用后续会大批量申请内存非常占用,而weak_ptr又不稳定,为了解决深拷贝的问题,可以使用单例模式或者原型模式,进行共享部分的复用。
示例说明
下面给出两个典型的原型模式的示例,分别为“周报”和“简历”。
示例 1:周报
解释:每周要填写周报,内容格式固定。周报分为几个部分,例如工作内容、进度、问题、计划等,这些部分可能需要不断重复使用和填写。如果每次都需要手动填写,那么效率就会比较低。因此,这个时候,我们可以通过原型模式来解决这个问题。
首先,我们定义一个周报类,包含如下内容:
class WeeklyReport {
public:
string name;
string date;
vector<string> workContent;
vector<string> progress;
vector<string> problems;
vector<string> plan;
ICloneable* Clone() {
WeeklyReport* report = new WeeklyReport(*this);
return report;
}
};
同时,定义一个周报管理类,负责管理周报模板和实例:
class WeeklyReportManager {
public:
unordered_map<string, WeeklyReport*> templates;
// 注册模板
void RegisterTemplate(string name, WeeklyReport* report) {
templates[name] = report;
}
// 获取模板副本
WeeklyReport* GetTemplate(string name) {
return (WeeklyReport*)templates[name]->Clone();
}
};
在运行时,我们只需加载多个周报模板,并且把需要的模板复制后修改,就可以快速地完成周报的填写。
示例 2:简历
解释:每次求职时,都需要撰写一份新的简历。如果每次都需要从头开始撰写,那么很难避免繁琐的重复劳动。同时,简历中的很多内容可能是可以公用的,例如教育经历、工作经历、项目经历等。这个时候,我们可以通过原型模式来实现简历的快速生成。
首先,我们定义一个简历类,包含如下内容:
class Resume {
public:
string name;
string email;
vector<Education*> educations;
vector<Work*> works;
vector<Project*> projects;
ICloneable* Clone() {
Resume* resume = new Resume(*this);
return resume;
}
};
同时,定义一个简历模板管理类,负责管理简历模板和实例:
class ResumeTemplateManager {
public:
unordered_map<string, Resume*> templates;
// 注册模板
void RegisterTemplate(string name, Resume* resume) {
templates[name] = resume;
}
// 获取模板副本
Resume* GetTemplate(string name) {
return (Resume*)templates[name]->Clone();
}
};
在运行时,我们只需加载多个简历模板,并且把需要的模板复制后修改,就可以快速地完成简历的撰写。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++设计模式之原型模式 - Python技术站