05创建型设计模式——原型模式

一、原型模式简介

原型模式(Prototype Pattern)模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例。使用原型模式创建的实例,具有与原型一样的数据。

1)由原型对象自身创建目标对象。换句话说,对象创建这一动作发自原型对象本身。

2)目标对象是原型对象的一个克隆(clone)。也就是说,通过原型模式创建的对象,不仅与原型对象具有相同的结构,还与原型对象具有相同的值。

原型模式具有浅克隆和深克隆之分。***深浅克隆的原型模式各有各的优势。

  • 浅克隆:复制对象的所有属性,但对于引用类型属性,仅复制引用。这会使得新对象与原始对象共享引用类型的内存空间。
  • 深克隆:复制对象的所有属性,即使是引用类型,也会创建新的实体,并复制指向这些实体的引用。这样新对象就与原始对象完全独立了。
原型模式的结构图

二、原型模式的用处

原型模式适用于需要多个一模一样的复杂对象 ,让其具有自我复制功能,统一一套接口。

原型模式说白了它允许对象复制现有的对象,而不是每次都通过外部new关键字来创建新的对象。这种模式有助于减少对象的创建时间,特别是当对象初始化的成本较高时。原型模式的关键在于它使用一个原型对象来创建新的实例,而不是通过直接生成它们。

原型模式优点:

  1. 对象初始化成本较高,可以避免通过复制现有对象来减少创建时间。
  2. 避免编写复杂的工厂方法来生成各种对象。
  3. 当你想支持对象的深克隆或浅克隆时。

原型模式注意点:

  • 当原型及其后代需要支持克隆行为时,这种方法非常有效。
  • 如果没有对象的克隆能力,或者你的领域不支持"克隆",原型模式可能不合适。
  • 使用原型模式时要确保能够处理循环引用的情况。

三、原型模式的设计方法

浅克隆的原型模式

背景:假设我们有一个电子邮件系统,其中每封邮件都包含一个附件。当我们想要复制一封邮件时,我们可能不需要复制附件的实际内容,而只需要复制邮件的其他信息和对附件的引用。这种情况下,浅克隆是非常合适的选择。

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;
}

运行效果

四、总结

原型模式的深拷贝和浅拷贝技术其实就是利用了指针的特性,指针本身具有指向的作用,当指针指向了堆区没有任何方法去拷贝或析构该堆区的数据,就造成了多个指针指向同一个堆区空间或多次析构的问题。

相关推荐
lb36363636361 小时前
分享一下arr的意义(c基础)(必看)(牢记)
c语言·知识点
南东山人3 小时前
一文说清:C和C++混合编程
c语言·c++
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
Ysjt | 深6 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__6 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
Microsoft Word6 小时前
c++基础语法
开发语言·c++·算法
一只小小汤圆7 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz7 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE7 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++