C++设计模式之原型模式(prototype)(创建型)

一、原型(Prototype)模式概述

DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝(既clone)。可以说,拷贝(clone)是原型模式的精髓所在。其中拷贝分为深拷贝和浅拷贝。浅复制:被复制的对象的所有变量都含有与原来的对象相同的值,而所有对其他对象的引用都仍然指向原来的对象。深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。Clone的时候使用深复制。

创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,而且也不需要知道任何创建的细节。这无疑是一种非常有效的方式,快速的创建一个新的对象。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,有对性能是大大的提高。因为如果不用Clone,每次new,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就实在是太低效了。

二、原型模式的优点和缺点及常见可用场景

1、原型模式的优点

  • 性能优良,原型模式是在内存二进制流的拷贝,当new的对象比较复杂的时候,要比直接new一个对象性能好很多,特别是在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点。
  • 原型模式允许在运行时动态改变具体的实现类型。原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
  • 对客户隐藏制造新实例的复杂性

2、原型模式的缺点

  • 由于是直接在内存中拷贝,构造函数不会执行。
  • 原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候,耦合度较高。

3、适合使用原型模式的场景及注意事项

  • 当通过初始化对象时需要消耗非常多的资源的时候,包括软硬件资源
  • 当通过new 一个对象需要准备繁琐的数据或复杂的权限时
  • 当一个对象需要提供给其他对象访问的时候,而且各个调用者可能会改变其属性的时候可以考虑使用原型模式构造多个副本对象供其他对象调用,又称为保护性拷贝。
  • 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。即final修饰的不会被拷贝。

例子:找工作时,需要准备简历。假如没有打印设备,需手写简历,其简历内容都是完全相同的内容。这样有个缺陷,如果要修改简历中的某项内容(比如对于通信公司的简历想突出强调开发过4G xxx项目,5G xxx项目,对于C++要求高的公司想强调“精通”STL和设计模式。。。),那么所有已写好的简历都要修改,工作量很大。随着科技的进步,出现了打印设备。我们只需手写一份,然后利用打印设备复印多份即可。如果要修改简历中的某项,那么修改原始的版本就可以了,然后再复印。原始的那份手写稿相当于是一个原型,有了它,就可以通过复印(拷贝)创造出更多的新简历。这就是原型模式的基本思想。下面给出原型模式的UML图,以刚才那个例子为实例。

4、ULM图

#include <iostream>
#include <string>
#include <cstddef>
#include <memory>

class Resume
{
protected:
	std::string name;
	std::string skill;
	//年龄,工作年限,项目等省略...
public:
	Resume() = default;
	Resume(const std::string& name, const std::string& skill)
	: name{name}
	, skill{skill}
	{
	}
	virtual ~Resume() = default;
	virtual void setName(const std::string&) = 0;
	virtual void setSkill(const std::string&) = 0;
	virtual void show() = 0;
	virtual std::unique_ptr<Resume> clone() = 0;
};

class ResumeA : public Resume
{
public:
	ResumeA(const std::string& name, const std::string& skill) :Resume{name, skill}
	{
		this->name = name;
		this->skill = skill;
	}
	ResumeA(const ResumeA& rhs)
	{
		this->name = rhs.name;
		this->skill = rhs.skill;
	}

	~ResumeA() = default;

	void setName(const std::string& name)
	{ 
		this->name = name; 
	}
	void setSkill(const std::string& skill) 
	{ 
		this->skill = skill; 
	}
	void show()
	{
		std::cout << "ResumeA name : " << name << ", skill: " << skill << std::endl;
	}
	std::unique_ptr<Resume> clone()    //克隆,关键所在
	{
		return std::make_unique<ResumeA>(*this);
	}
};

int main()
{
	auto r1 = new ResumeA("PersionA", "精通C,熟悉C++...");
	auto r3 = r1->clone();
	r1->show(); 

	//想去面试C++要求高的公司
	r3->setSkill("精通C++ STL, design pattern...");
	r3->show();

	return 0;
}

结果如下:

 

另外我们也可以把工作经验放到一个单独的类中,摘自:https://blog.csdn.net/xiqingnian/article/details/41983337

 Prototype.h

#ifndef PROTOTYPE_H
#define PROTOTYPE_H
 
#include <iostream>
#include <string>
 
//工作经验类:WorkExperience类
class WorkExperience
{
private:
	std::string workDate;
	std::string company;
public:
	WorkExperience(){}
	~WorkExperience(){}
	//供克隆调用的构造函数
	WorkExperience(WorkExperience* work)
	{
		this->workDate=work->workDate;
		this->company=work->company;
	}
	std::string getWorkDate()
	{
		return workDate;
	}
	void setWorkDate(std::string workDate)
	{
		this->workDate=workDate;
	}
	std::string getCompany()
	{
		return company;
	}
	void setCompany(std::string company)
	{
		this->company=company;
	}
 
	//克隆
	WorkExperience*  Clone()
	{
		//调用供克隆调用的构造函数WorkExperience(WorkExperience)
		return new WorkExperience(this);
	}
};
 
//原型类,声明一个克隆自身的接口。
class Prototype
{
protected:
	std::string name;
	std::string sex;
	std::string age;
	std::string timeArea;
	std::string company;
public:
	virtual void SetPersonalInfo(std::string sex,std::string age)=0;
	virtual void Display()=0;
	virtual Prototype* Clone()=0;
 
};
 
//具体原型类,即ConcretePrototype类,本例中为简历Resume类,实现一个克隆自身的操作。
class Resume:public Prototype
{
private:
	WorkExperience* work;
public:
	Resume(std::string name)
	{
		this->name=name;
		work=new WorkExperience();
	}
	~Resume()
	{
		if(work!=NULL)
			delete work;
	}
	//供克隆调用的构造函数
	Resume(WorkExperience* work)
	{
		this->work=work->Clone();
	}
	void SetPersonalInfo(std::string sex,std::string age)
	{
		this->sex=sex;
		this->age=age;
	}
	void SetWorkExperience(std::string workDate,std::string company)
	{
		work->setWorkDate(workDate);
		work->setCompany(company);
	}
	void Display()
	{
		std::cout<<name<<"  "<<sex<<"  "<<age<<std::endl;
		std::cout<<"工作经历  "<<work->getWorkDate()<<"  "<<work->getCompany()<<std::endl;
	}
 
	//克隆
	Resume* Clone()
	{
		//调用 供克隆函数调用的构造函数Resume(work)
		Resume *cloneResume=new Resume(work);
		cloneResume->name=this->name;
		cloneResume->age=this->age;
		cloneResume->sex=this->sex;
		return cloneResume;
	}
};
 
 
#endif

main.cpp

#include "Prototype.h"
#include <iostream>
#include <stdlib.h>
 
void main()
{
    Resume* a=new Resume("大鸟");
    a->SetPersonalInfo("男","29");
    a->SetWorkExperience("1998-2000","XX公司");
 
    Resume* b=a->Clone();
    b->SetWorkExperience("1998-2006","YY公司");
 
    Resume* c=b->Clone();
    c->SetPersonalInfo("男","24");
    c->SetWorkExperience("1998-2003","ZZ公司");
 
    a->Display();
    b->Display();
    c->Display();
 
    delete a;
    delete b;
    delete c;
    a=b=c=NULL;
    system("pause");
}

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
原型模式Prototype Pattern)是一种创建设计模式,它允许通过复制现有对象来创建新对象,而无需通过显式的实例化过程。原型模式通过克隆(clone)已有对象来创建新对象,从而避免了使用传统的构造函数创建对象的开销。 在C++中,原型模式可以通过实现一个可克隆接口(通常称为原接口)来实现。这个接口通常包含一个克隆方法,用于复制当前对象并返回一个新的副本。派生类可以实现这个接口来定义自己的克隆逻辑。 以下是原型模式的一般实现步骤: 1. 创建一个原接口(或基类): ``` class Prototype { public: virtual Prototype* clone() const = 0; virtual void setAttributes(...) = 0; virtual void print() const = 0; }; ``` 2. 实现原接口的具体类(或派生类): ``` class ConcretePrototype : public Prototype { private: // 在派生类中定义特定的属性 // ... public: Prototype* clone() const override { return new ConcretePrototype(*this); } void setAttributes(...) override { // 设置属性值 } void print() const override { // 打印属性值 } }; ``` 3. 在客户端代码中使用原型模式: ``` Prototype* original = new ConcretePrototype(); original->setAttributes(...); Prototype* clone = original->clone(); clone->print(); delete original; delete clone; ``` 通过使用原型模式,我们可以避免在每次创建对象时重复执行初始化的过程,提高了对象的创建效率。此外,原型模式还允许我们在运行时动态地添加或删除对象的属性,并通过克隆来创建新对象。 需要注意的是,在实现原类时,需要确保所有成员变量都能正确地被拷贝(或克隆)。有时候可能需要自定义拷贝构造函数和赋值运算符来实现深拷贝,以避免浅拷贝带来的问题。 总结起来,原型模式通过克隆已有对象来创建新对象,提供了一种简单且灵活的对象创建方式。它适用于那些对象的创建过程比较复杂或开销较大的情况下。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值