一、原型模式简介
原型模式(Prototype Pattern)模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例。使用原型模式创建的实例,具有与原型一样的数据。
1)由原型对象自身创建目标对象。换句话说,对象创建这一动作发自原型对象本身。
2)目标对象是原型对象的一个克隆(clone)。也就是说,通过原型模式创建的对象,不仅与原型对象具有相同的结构,还与原型对象具有相同的值。
原型模式具有浅克隆和深克隆之分。***深浅克隆的原型模式各有各的优势。
- 浅克隆:复制对象的所有属性,但对于引用类型属性,仅复制引用。这会使得新对象与原始对象共享引用类型的内存空间。
- 深克隆:复制对象的所有属性,即使是引用类型,也会创建新的实体,并复制指向这些实体的引用。这样新对象就与原始对象完全独立了。
原型模式的结构图
二、原型模式的用处
原型模式适用于需要多个一模一样的复杂对象 ,让其具有自我复制功能,统一一套接口。
原型模式说白了它允许对象复制现有的对象,而不是每次都通过外部new关键字来创建新的对象。这种模式有助于减少对象的创建时间,特别是当对象初始化的成本较高时。原型模式的关键在于它使用一个原型对象来创建新的实例,而不是通过直接生成它们。
原型模式优点:
- 对象初始化成本较高,可以避免通过复制现有对象来减少创建时间。
- 避免编写复杂的工厂方法来生成各种对象。
- 当你想支持对象的深克隆或浅克隆时。
原型模式注意点:
- 当原型及其后代需要支持克隆行为时,这种方法非常有效。
- 如果没有对象的克隆能力,或者你的领域不支持"克隆",原型模式可能不合适。
- 使用原型模式时要确保能够处理循环引用的情况。
三、原型模式的设计方法
浅克隆的原型模式
背景:假设我们有一个电子邮件系统,其中每封邮件都包含一个附件。当我们想要复制一封邮件时,我们可能不需要复制附件的实际内容,而只需要复制邮件的其他信息和对附件的引用。这种情况下,浅克隆是非常合适的选择。
shallow_Prototype.cpp
cpp
#include <iostream>
#include <cstring>
//附件
class Attachment {
private:
std::string fileName;
public:
Attachment(const std::string& name) : fileName(name) {}
std::string getFileName() const {
return fileName;
}
// 返回 fileName 的地址
const std::string* getFileNameAddress() const {
return &fileName;
}
Attachment* clone() const {
return new Attachment(*this);
}
};
//邮件
class Email {
public:
Email(const std::string& sender, const std::string& recipient, const std::string& body, Attachment* attachment)
: sender(sender), recipient(recipient), body(body), attachment(attachment) {}
Email(const Email& other)
: sender(other.sender), recipient(other.recipient), body(other.body), attachment(other.attachment) {}
~Email() {
// 注意:这里没有删除附件,因为我们假设多个邮件共享同一个附件
// 如果实现深拷贝,需要在此处删除附件
}
Email* clone() const {
return new Email(*this);
}
void print() const {
std::cout << "Sender: " << sender << std::endl;
std::cout << "Recipient: " << recipient << std::endl;
std::cout << "Body: " << body << std::endl;
std::cout << "Attachment: " << (attachment ? attachment->getFileName() : "None") << std::endl;
}
Attachment* getAttachment() const {
return attachment;
}
private:
std::string sender;
std::string recipient;
std::string body;
Attachment* attachment;
};
//模拟接口
void doWorking() {
// 创建附件对象
Attachment* attachment = new Attachment("document.pdf");
// 创建邮件对象
Email originalEmail("alice@example.com", "bob@example.com", "Hi Baby!", attachment);
// 克隆邮件对象
Email* clonedEmail = originalEmail.clone();
// 打印原始邮件和克隆邮件的信息
std::cout << "Original Email:" << std::endl;
originalEmail.print();
std::cout << std::endl << "Cloned Email:" << std::endl;
clonedEmail->print();
// 检查附件对象是否被共享(共享说明是浅克隆)
std::cout << std::endl << "原始附件: " << originalEmail.getAttachment()->getFileName() << " 原始附件地址: " << originalEmail.getAttachment()->getFileNameAddress() << std::endl;
std::cout << "深克隆附件: " << clonedEmail->getAttachment()->getFileName() << " 深克隆附件地址: " << clonedEmail->getAttachment()->getFileNameAddress() << std::endl;
// 清理资源
delete clonedEmail;
return ;
}
int main() {
//调用
doWorking();
return 0;
}
运行效果
深克隆的原型模式
deep_Prototype.cpp
cpp
#include <iostream>
#include <cstring>
//附件
class Attachment {
private:
std::string fileName;
public:
Attachment(const std::string& name) : fileName(name) {}
std::string getFileName() const {
return fileName;
}
// 返回 fileName 的地址
const std::string* getFileNameAddress() const {
return &fileName;
}
Attachment* clone() const {
return new Attachment(*this);
}
};
//邮件
class Email {
public:
Email(const std::string& sender, const std::string& recipient, const std::string& body, Attachment* attachment)
: sender(sender), recipient(recipient), body(body), attachment(attachment ? attachment->clone():nullptr) {}
Email(const Email& other)
: sender(other.sender), recipient(other.recipient), body(other.body), attachment(other.attachment ? other.attachment->clone():nullptr) {}
~Email() {
delete attachment;// 删除附件,避免内存泄漏
}
Email* clone() const {
return new Email(*this);
}
void print() const {
std::cout << "Sender: " << sender << std::endl;
std::cout << "Recipient: " << recipient << std::endl;
std::cout << "Body: " << body << std::endl;
std::cout << "Attachment: " << (attachment ? attachment->getFileName() : "None") << std::endl;
}
Attachment* getAttachment() const {
return attachment;
}
private:
std::string sender;
std::string recipient;
std::string body;
Attachment* attachment;
};
//模拟接口
void doWorking() {
// 创建附件对象
Attachment* attachment = new Attachment("document.pdf");
// 创建邮件对象
Email originalEmail("alice@example.com", "bob@example.com", "Hi Baby!", attachment);
// 克隆邮件对象
Email* clonedEmail = originalEmail.clone();
// 打印原始邮件和克隆邮件的信息
std::cout << "Original Email:" << std::endl;
originalEmail.print();
std::cout << std::endl << "Cloned Email:" << std::endl;
clonedEmail->print();
std::cout << std::endl << "原始附件: " << originalEmail.getAttachment()->getFileName() << " 原始附件地址: " << originalEmail.getAttachment()->getFileNameAddress() << std::endl;
std::cout << "深克隆附件: " << clonedEmail->getAttachment()->getFileName() << " 深克隆附件地址: " << clonedEmail->getAttachment()->getFileNameAddress() << std::endl;
// 清理资源
delete clonedEmail;
delete attachment;
return ;
}
int main() {
//调用
doWorking();
return 0;
}
运行效果
四、总结
原型模式的深拷贝和浅拷贝技术其实就是利用了指针的特性,指针本身具有指向的作用,当指针指向了堆区没有任何方法去拷贝或析构该堆区的数据,就造成了多个指针指向同一个堆区空间或多次析构的问题。