C++设计模式-原型模式 原创 设计模式 2021年10月18日 15:54 夏至未至 1162 当前内容 9011 字,在路上,马上到,马上到 ### 目录 [TOC] ### 原型模式介绍 #### 什么是原型模式 原型模式(Prototype Pattern)是用于`创建重复的对象`,同时又能保证性能。这种类型的设计模式属于创建型模式,`它提供了一种创建对象的最佳方式`。 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。 #### 为何使用原型模式 原型模式(Prototype Pattern): 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。主要解决在运行期建立和删除原型的问题 #### 如何实现原型模式 利用已有的一个原型对象,快速地生成和原型对象一样的实例。 1. 实现克隆操作,也就是 clone() 2. 能够隔离类对象的使用者和具体类型之间的耦合关系,要求这些具体类型拥有稳定的接口。 #### 何时使用原型模式 1. 创建新对象成本较大(如一些初始化需要较长时间,占用太多的CPU资源或网络资源),并且新的对象可以通过原型模式对已有对象进行复制来获得,如有必要可稍作修改。 2. 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。 举例:一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。 #### 使用具体场景 ##### 生活中 细胞分裂;克隆羊;抄作业 ##### 软件中 JAVA 中的 Object clone() 方法;工作周报、绩效 、邮件等拷贝上次 ### 模式特点 #### 优点 1. 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,提高新实例的创建效率。 2. 扩展性较好,模式中提供了抽象原型类,具体原型类可根据需要扩展。 3. 原型模式提供了简化的创建结构,模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。 #### 缺点 1. 需要为每一个类配备一个克隆方法,该克隆方法位于一个类的内部,改造已有类时需要修改源代码,违背开闭原则; 2. 在实现深克隆时需要编写较为复杂的代码,并且如果对象嵌套很多引用时,为了实现深拷贝每一层嵌套都必须支持深克隆。 ### 深浅拷贝 #### 浅拷贝 只是增加了一个指针指向已存在的内存地址,如果原地址发生改变,那么浅拷贝出来的对象也会相应的改变。相当于一个箱子有多个钥匙,其中某人打开箱子取走箱子里的东西时,其他人再打开箱子看到的都是空箱子。 #### 深拷贝 是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。相当有多个箱子每个箱子对应一个钥匙,一个人取走他的钥匙对应的箱子里的东西时,不会对其他人产生影响。 ### 模式结构 - 抽象原型类(AbstractPrototype):规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定) - 具体原型类(ConcretePrototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象,需要实现抽象原型角色所要求的接口。 - 客户端(Client):客户端中声明一个抽象原型类,根据客户需求clone具体原型类对象实例 ### 实操代码 场景:在已有邮件上修改生成需要发送的新邮件 文件名:原型模式.cpp #### 上机源码 #include // 场景:在已有邮件上修改生成需要发送的新邮件 // 抽象原型类 class AbstractPrototypeMail { public: virtual ~AbstractPrototypeMail() = default; // 克隆对象(内部为深拷贝) virtual AbstractPrototypeMail* clone() = 0; // 展示邮件 virtual void showMail() = 0; // 改变邮件标题 virtual void changeTitle(std::string title) = 0; // 改变发送者 virtual void changeSender(std::string sender) = 0; // 改变收件人 virtual void changeRecipients(std::string rec) = 0; // 改变邮件内容 virtual void changeBody(std::string body) = 0; // 改变附件 virtual void changeAtt(std::string name) = 0; protected: AbstractPrototypeMail() = default; }; // 附件类 class Attachment { public: Attachment() { std::cout << "邮件附件被创建" << std::endl; } ~Attachment() { std::cout << "邮件附件被销毁" << std::endl; } void changeName(const std::string& name) { // 改变附件名称 nameAtt = name; }; std::string getName() { // 获取附件名 return nameAtt; } private: std::string nameAtt; // 附件名称 }; // 具体原型类 class ConcretePrototypeMail : public AbstractPrototypeMail { public: ConcretePrototypeMail(const std::string& title, const std::string& sender, const std::string& rec, const std::string& body, const std::string& nameAtt = "") { std::cout << "邮件被创建" << std::endl; mailTitle = title; mailSender = sender; mailRecipients = rec; mailBody = body; if (!nameAtt.empty()) { mailAtta = new Attachment(); mailAtta->changeName(nameAtt); } } ~ConcretePrototypeMail() override { std::cout << "邮件被销毁" << std::endl; if (mailAtta != nullptr) { delete(mailAtta); } } // 新对象创建复杂,故封装 AbstractPrototypeMail* clone() override { // 注意:当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,提高新实例的创建效率 auto* newMail = new ConcretePrototypeMail(mailTitle, mailSender, mailRecipients, mailBody, mailAtta->getName()); return newMail; } void showMail() override { std::cout << "邮件标题: " << mailTitle << std::endl; std::cout << "邮件发送者: " << mailSender << std::endl; std::cout << "邮件接收者: " << mailRecipients << std::endl; std::cout << "邮件内容: " << mailBody << std::endl; std::cout << "邮件附件名: " << mailAtta->getName() << std::endl; } void changeTitle(const std::string title) override { mailTitle = title; } void changeSender(const std::string sender) override { mailSender = sender; }; void changeRecipients(const std::string rec) override { mailRecipients = rec; } void changeBody(const std::string body) override { mailBody = body; } void changeAtt(const std::string name) override { // 这里其实也是一个深拷贝的体现 if (mailAtta != nullptr) { delete mailAtta; mailAtta = nullptr; } mailAtta = new Attachment(); mailAtta->changeName(name); } private: std::string mailTitle; // 邮件标题 std::string mailSender; // 邮件发送人 std::string mailRecipients; // 邮件接收人,暂定一个人 std::string mailBody; // 邮件内容 Attachment* mailAtta; // 邮件附件(类对象) }; #### 浅拷贝测试 int main() { std::cout << "网站:https://www.codecomeon.com/" << std::endl; std::cout << std::endl; // 初始邮件 auto* originalMail = new ConcretePrototypeMail("原标题", "原发送者", "原接收者", "原内容", "原附件"); std::cout << "初始邮件地址: " << originalMail << std::endl; originalMail->showMail(); std::cout << std::endl; // 浅拷贝演示 std::cout << "**************浅拷贝演示**************" << std::endl; auto* copyMail_A = originalMail; copyMail_A->changeTitle("新标题"); copyMail_A->changeSender("新发送者"); copyMail_A->changeRecipients("新接收者"); copyMail_A->changeBody("新内容"); copyMail_A->changeAtt("新附件"); std::cout << "浅拷贝邮件展示:" << std::endl; std::cout << "地址: " << copyMail_A << std::endl; std::cout << std::endl; // 浅拷贝,新旧邮件一样,故桥拷贝,会改变原邮件 std::cout << "新邮件"<< std::endl; copyMail_A->showMail(); std::cout << "老邮件" << std::endl; originalMail->showMail(); std::cout << std::endl; delete originalMail; originalMail = nullptr; return 0; } ##### 浅拷贝执行输出 网站:https://www.codecomeon.com/ 邮件被创建 邮件附件被创建 初始邮件地址: 00E36338 邮件标题: 原标题 邮件发送者: 原发送者 邮件接收者: 原接收者 邮件内容: 原内容 邮件附件名: 原附件 **************浅拷贝演示************** 邮件附件被销毁 邮件附件被创建 浅拷贝邮件展示: 地址: 00E36338 新邮件 邮件标题: 新标题 邮件发送者: 新发送者 邮件接收者: 新接收者 邮件内容: 新内容 邮件附件名: 新附件 老邮件 邮件标题: 新标题 邮件发送者: 新发送者 邮件接收者: 新接收者 邮件内容: 新内容 邮件附件名: 新附件 邮件被销毁 邮件附件被销毁 E:\DesignPatterns\DesignPatterns\Debug\DesignPatterns.exe (进程 72364)已退出,代码为 0。 按任意键关闭此窗口. . . #### 深拷贝测试 int main() { std::cout << "网站:https://www.codecomeon.com/" << std::endl; std::cout << std::endl; // 初始邮件 auto* originalMail = new ConcretePrototypeMail("原标题", "原发送者", "原接收者", "原内容", "原附件"); std::cout << "初始邮件地址: " << originalMail << std::endl; originalMail->showMail(); std::cout << std::endl; // 深拷贝演示 std::cout << "**************深拷贝演示**************" << std::endl; auto* copyMail_B = originalMail->clone(); copyMail_B->changeTitle("新标题"); copyMail_B->changeSender("新发送者"); copyMail_B->changeRecipients("新接收者"); copyMail_B->changeBody("新内容"); copyMail_B->changeAtt("新附件"); std::cout << "深拷贝邮件展示:" << std::endl; std::cout << "地址: " << copyMail_B << std::endl; std::cout << std::endl; // 深拷贝,新旧邮件不一样,成功拷贝邮件 std::cout << "新邮件" << std::endl; copyMail_B->showMail(); std::cout << "老邮件" << std::endl; originalMail->showMail(); std::cout << std::endl; delete originalMail; originalMail = nullptr; return 0; } ##### 深拷贝执行输出 网站:https://www.codecomeon.com/ 邮件被创建 邮件附件被创建 初始邮件地址: 00831300 邮件标题: 原标题 邮件发送者: 原发送者 邮件接收者: 原接收者 邮件内容: 原内容 邮件附件名: 原附件 **************深拷贝演示************** 邮件被创建 邮件附件被创建 邮件附件被销毁 邮件附件被创建 深拷贝邮件展示: 地址: 008313A8 新邮件 邮件标题: 新标题 邮件发送者: 新发送者 邮件接收者: 新接收者 邮件内容: 新内容 邮件附件名: 新附件 老邮件 邮件标题: 原标题 邮件发送者: 原发送者 邮件接收者: 原接收者 邮件内容: 原内容 邮件附件名: 原附件 邮件被销毁 邮件附件被销毁 E:\DesignPatterns\DesignPatterns\Debug\DesignPatterns.exe (进程 11524)已退出,代码为 0。 按任意键关闭此窗口. . . 本文标题: C++设计模式-原型模式 本文作者: 夏至未至 发布时间: 2021年10月18日 15:54 最近更新: 2022年2月9日 11:10 原文链接: 许可协议: 署名-非商业性-禁止演绎 4.0 国际(CC BY-NC-ND 4.0) 请按协议转载并保留原文链接及作者 设计模式(25) 上一个 C++设计模式-建造者模式 下一个 C++设计模式-抽象工厂模式 当前文章评论暂未开放,请移步至留言处留言。