深入浅出设计模式——创建型模式之原型模式 Prototype

发布于:2025-08-01 ⋅ 阅读:(21) ⋅ 点赞:(0)


代码仓库

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

定义看起来有点绕口,不妨简单的理解为:原型模式就是用来克隆对象的。

举个例子,比如有一天,周杰伦到奶茶店点了一份不加冰的原味奶茶,你说我是周杰伦的忠实粉,我也要一份跟周杰伦一样的。
在这里插入图片描述

即便Jungle读书少,Jungle也清晰地记得中学生物课本上提到过的克隆羊“多利”。虽然多利寿命不长,但它的出现对“克隆(Clone)”技术意义重大。克隆,直观说就是从原有生物体上取体细胞,然后无性繁殖出有完全相同基因的个体或种群。这么说来中国的克隆技术其实是世界领先的,因为孙悟空拔一根毫毛变出许多一模一样的孙悟空的传说本质上就是克隆!而本文将要介绍的原型模式,将克隆技术应用到了软件设计层面。

原型模式简介

原型模式通过复制一个已有对象来获取更多相同或者相似的对象。原型模式定义如下:

使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

原型模式的工作原理是将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程。从工厂方法角度而言,创建新对象的工厂就是原型类自己。软件系统中有些对象的创建过程比较复杂,且有时需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。

原型模式结构

在这里插入图片描述

关于克隆方法:浅拷贝/深拷贝

在这里插入图片描述

原型模式代码实例

明天就是周一了,Jungle又陷入了苦恼中,因为作业还没完成。于是Jungle想拿着哥哥Single的作业来抄一份。虽然抄袭作业并不好,但是边抄边学借鉴一下也是可以的。于是乎,Jungle开始动起手来……

作业包含几个部分:姓名(name)、学号(idNum)、模型(workModel)。首先定义一个workModel类:

// work model类
// 作为复杂的成员对象,供ConcreteWork引用。
class WorkModel {
public:
    std::string modelName;
    WorkModel() : modelName("") {}
    WorkModel(const std::string& iName) : modelName(iName) {}
    void setWorkModelName(const std::string& iName) {
        this->modelName = iName;
    }

    std::string getWorkModelName() const {
        return modelName;
    }

    // 深拷贝构造函数
    // 深拷贝构造函数防止多个对象共享同一个内存(避免浅拷贝问题)。
    // std::string 本身已经重载了拷贝构造函数和赋值运算符,实现了深拷贝的语义。
    WorkModel(const WorkModel& other) : modelName(other.modelName) {}
};  

该实例UML图如下:
在这里插入图片描述

定义原型类和克隆方法

// 抽象原型类PrototypeWork
// 定义抽象接口clone(),具体原型类必须实现该方法。
class PrototypeWork {
public:
    PrototypeWork() {}
    virtual ~PrototypeWork() {}
    virtual PrototypeWork* clone() = 0;
    virtual void printWorkInfo() const = 0;
};

// 具体原型类PrototypeWork
class ConcreteWork: public PrototypeWork {
public:
    ConcreteWork(const string& iName, int iIdNum, const string& modelName)
        : name(iName), idNum(iIdNum), workModel(new WorkModel(modelName)) {}

    // 深拷贝构造函数
    ConcreteWork(const ConcreteWork& other)
        : name(other.name), idNum(other.idNum), workModel(new WorkModel(*other.workModel)) {}

    // 深拷贝赋值运算符
    // ConcreteWork& operator=(const ConcreteWork& other) {
    //     if (this != &other) {
    //         name = other.name;
    //         idNum = other.idNum;
    //         delete workModel;
    //         workModel = new WorkModel(*other.workModel);
    //     }
    //     return *this;
    // }

    // 克隆接口实现(返回深拷贝)
    PrototypeWork* clone() override {
        return new ConcreteWork(*this);
    }

    ~ConcreteWork() {
        delete workModel;
    }

    // 打印work信息
	void printWorkInfo() const override {
        std::cout << "Name: " << name << std::endl;
        std::cout << "IdNum: " << idNum << std::endl;
        std::cout << "ModelName: " << workModel->getWorkModelName() << std::endl;
    }

    // 新增set方法
    void setName(const std::string& newName) {
        name = newName;
    }

    void setIdNum(int newIdNum) {
        idNum = newIdNum;
    }

    void setModel(WorkModel* newModel) {
        if (workModel != nullptr) {
            delete workModel;
        }
        workModel = newModel;
    }

    // 新增get方法
    std::string getName() const {
        return name;
    }

    int getIdNum() const {
        return idNum;
    }

    WorkModel* getModel() const {
        return workModel;
    }

private:
    string name;
    int idNum;
    WorkModel* workModel;
};

客户端使用代码示例

示例一:浅拷贝

#include "PrototypePattern.h"

int main() {
// #if 0
// // 下面的代码将不会被编译,也不会执行
	ConcreteWork *singleWork = new ConcreteWork("Single",1001,"Single_Model");
	printf("\nSingle的作业:\n");
	singleWork->printWorkInfo();
	
	printf("\njungle直接抄作业……\n");
	ConcreteWork *jungleWork = singleWork;
	printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();

	// 抄完改名字和学号,否则会被老师查出来
	printf("\njungle抄完改名字和学号,否则会被老师查出来……\n");
	jungleWork->setName("jungle");
	jungleWork->setIdNum(1002);
	WorkModel *jungleModel = new WorkModel();
	jungleModel->setWorkModelName("Jungle_Model");
	jungleWork->setModel(jungleModel);
	
	// 检查下是否改对了
	printf("\nSingle的作业:\n");
	singleWork->printWorkInfo();
	printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();
// #endif 
#if 0
// 下面的代码将不会被编译,也不会执行
	ConcreteWork *singleWork = new ConcreteWork("Single", 1001, "Single_Model");
	printf("\nSingle的作业:\n");

	// clone() 返回 PrototypeWork*,需类型转换
    ConcreteWork* jungleWork = dynamic_cast<ConcreteWork*>(singleWork->clone());
	printf("\njungle直接抄作业……\n");

	// 抄完改名字和学号,否则会被老师查出来
	printf("\njungle抄完改名字和学号,否则会被老师查出来……\n");
	jungleWork->setName("jungle");
	jungleWork->setIdNum(1002);
	WorkModel *jungleModel = new WorkModel();
	jungleModel->setWorkModelName("Jungle_Model");
	jungleWork->setModel(jungleModel);

	// 检查下是否改对了
	printf("\nSingle的作业:\n");
	singleWork->printWorkInfo();
	printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();

	delete singleWork;
	delete jungleWork;
#endif 
	return 0;
}

在这里插入图片描述
显然,这不是我们想要的结果。接下来我们使用clone方法。

示例二:深拷贝


```cpp
#include "PrototypePattern.h"

int main() {
#if 0
// 下面的代码将不会被编译,也不会执行
	ConcreteWork *singleWork = new ConcreteWork("Single",1001,"Single_Model");
	printf("\nSingle的作业:\n");
	singleWork->printWorkInfo();
	
	printf("\njungle直接抄作业……\n");
	ConcreteWork *jungleWork = singleWork;
	printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();

	// 抄完改名字和学号,否则会被老师查出来
	printf("\njungle抄完改名字和学号,否则会被老师查出来……\n");
	jungleWork->setName("jungle");
	jungleWork->setIdNum(1002);
	WorkModel *jungleModel = new WorkModel();
	jungleModel->setWorkModelName("Jungle_Model");
	jungleWork->setModel(jungleModel);
	
	// 检查下是否改对了
	printf("\nSingle的作业:\n");
	singleWork->printWorkInfo();
	printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();
#endif 
// #if 0
// 下面的代码将不会被编译,也不会执行
	ConcreteWork *singleWork = new ConcreteWork("Single", 1001, "Single_Model");
	printf("\nSingle的作业:\n");
    singleWork->printWorkInfo();

	// clone() 返回 PrototypeWork*,需类型转换
	printf("\njungle直接抄作业……\n");
    ConcreteWork* jungleWork = dynamic_cast<ConcreteWork*>(singleWork->clone());
    printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();

	// 抄完改名字和学号,否则会被老师查出来
	printf("\njungle抄完改名字和学号,否则会被老师查出来……\n");
	jungleWork->setName("jungle");
	jungleWork->setIdNum(1002);
	WorkModel *jungleModel = new WorkModel();
	jungleModel->setWorkModelName("Jungle_Model");
	jungleWork->setModel(jungleModel);

	// 检查下是否改对了
	printf("\nSingle的作业:\n");
	singleWork->printWorkInfo();
	printf("\nJungle的作业:\n");
	jungleWork->printWorkInfo();

	delete singleWork;
	delete jungleWork;
// #endif 
	return 0;
}

在这里插入图片描述

原型模式总结

在这里插入图片描述

开闭原则

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!


网站公告

今日签到

点亮在社区的每一天
去签到