大话设计模式 —— 第九章《原型模式》C++ 代码实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34536551/article/details/88998112

目录

概述

定义

优点

缺点

使用场景

注意事项

浅复制和深复制


概述


 在某些情况下,可能不希望反复使用类的构造函数创建许多不同的对象,而是希望使用该类创建一个对象后,以该对象为原型得到该对象的若干个副本 ( 分深拷贝 和 浅拷贝)。也就是说,当一个对象定义为原型对象时,该对象中提供了一个 Clone 方法,然后该原型对象调用此方法可以“克隆”原型对象得到一个新对象。原型对象 和“克隆”出的新对象可以分别独立地变化,也就是说,原型对象改变其状态不会影响到以它为原型“克隆”出的新对象,反之也一样。

  •   例如,通过复制一个已有的Word文档中的文本创建一个新的Word文档后,两个文档中的文本内容可独立地变化互不影响。

定义


  • 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,而且不需要知道任何创建的细节。
  • 属于创建型模式, 主要用于对象的拷贝
  • 在实际应用中,原型模式很少单独出现。经常与其他模式混用。
  •  

上面的定义其中有一个词很重要,那就是拷贝。可以说,拷贝是原型模式的精髓所在。举个现实中的例子来介绍原型模式。找工作的时候,我们需要准备简历。假设没有打印设备,因此需手写简历,这些简历的内容都是一样的。这样有个缺陷,如果要修改简历中的某项,那么所有已写好的简历都要修改,工作量很大。随着科技的进步,出现了打印设备。我们只需手写一份,然后利用打印设备复印多份即可。如果要修改简历中的某项,那么修改原始的版本就可以了,然后再复印。原始的那份手写稿相当于是一个原型,有了它,就可以通过复印(拷贝)创造出更多的新简历。这就是原型模式的基本思想。
 

原型模式的UML图
  • Client(客户端角色):调用 Prototype 来克隆自身,从而创建一个新对象。
  • Prototype(抽象原型角色):是抽象类或者接口,用来声明clone方法。该方法是用来克隆自身的接口。
  • ConcretePrototype(具体的原型类):是客户端角色使用的对象,即被复制的对象。需要实现克隆自身的具体操作。

需要注意的是,Prototype 在C++ 中是需要自己定义的,但是Java、C#中 实现了Cloneable接口,也就不需要自己定义,直接实现该接口即可。


优点


  • 性能优良。原型模式是在内存二进制流的拷贝,要比直接 new 一个对象在性能好很多。 特别是复制大对象时,性能的差别非常明显。
  • 没有构造函数的约束。由于拷贝直接在内存中进行,因此并不执行构造函数。
  • 简化对象的创建过程,隐藏了对象创建的细节,又对性能大大的提高

 因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。


缺点


  • 直接在内存中拷贝,构造函数是不会执行的,这样就减少了约束,这既是优点也是缺点,需要在实际应用中去考量。

使用场景

  • 如果类的初始化需要耗费较多的资源,那么可以通过原型拷贝避免这些消耗。
  • 如果某个对象new的过程中很耗时,比如说需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  • 一个对象需要提供给多个其它对象使用,而且各个调用者可能都需要修改其值时,则建议使用原型模式拷贝多个对象供调用者使用。
  • 一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,有对性能是大大的提高。因为如果不用Clone,每次new,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就实在是太低效了。

注意事项


  • 原型模式时通过拷贝一个现有的对象生成新对象的, 这点和通过一个类进行实例化来构造新对象不同.
  • 使用原型模式拷贝对象不会调用类的构造函数, 这是通过Prototype 的 Clone 方法来完成的. 它直接在内存中复制数据,所以该类的构造方法中的代码不会执行.  不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效,即该类的构造函数是private也同样可以在实现了原型模式, 这个和单例模式冲突.    单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。
  •  通过使用 clone() 方法产生的新对象,其构造方法不会被执行。因为 clone() 方法原理就是直接在内存中以二进制流的方式进行拷贝,重新分配了一个内存块,并不会调用到构造方法。

浅复制和深复制


  • 浅复制即是拷贝对象中存在除基本数据类型和 String 类型外的其它成员变量时( 比如C++ 中 指针和引用),新对象的成员变量的引用还是指向的原对象的成员变量.
  •   所谓深复制,就是复制出来的新对象,其中的引用数据类型的成员变量,其引用也是指向了新的内存区域,即修改其中一个,另一个也不会随之被修改。

下面是用C++代码实现大话设计模式的本章代码:

#include<iostream>
#include<string>
using namespace std;

class Prototype // Prototype(抽象原型角色):是抽象类或者接口,用来声明clone方法。该方法是用来克隆自身的接口。
{
public:
	virtual ~Prototype() = default;
	virtual Prototype* Clone() = 0;

};
class WorkExperience final :public Prototype
{// ConcretePrototype(具体的原型类):是客户端角色使用的对象,即被复制的对象。需要实现克隆自身的具体操作。
private:
	std::string m_workDate;
	std::string m_company;
public:
	string getWorkDate(){ return m_workDate; }
	string getCompany() { return m_company; }
	void setWorkDate(string workDate) { m_workDate = workDate; }
	void setCompany(string company) {m_company = company;}

	Prototype* Clone()override
	{
		WorkExperience *p = new WorkExperience;
		*p = *this;
		return p;
	}
};

class Resume final :public Prototype
{//ConcretePrototype(具体的原型类):是客户端角色使用的对象,即被复制的对象。需要实现克隆自身的具体操作。
private:
	std::string m_name;
	std::string m_sex;
	std::string m_age;

	WorkExperience *m_work = new WorkExperience;
	explicit Resume(WorkExperience *work) :m_work((WorkExperience*)work->Clone()) {}
public:
	Resume() = default;
	void SetPersonalInfo(string name, string sex, string age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
	}
	void SetWorkExperience(std::string workDate, std::string company)
	{
		m_work->setWorkDate(workDate);
		m_work->setCompany(company);
	}
	void Display()
	{
		cout << "姓名:" << m_name << " "
			<< " 性别:" << m_sex << " "
			<< " 年龄:" << m_age << endl;

		cout << "工作经历为:" << m_work->getWorkDate() << " " << m_work->getCompany() << endl << endl;
		
	}
	Prototype *Clone()override
	{
		Resume *cloneResume = new Resume(m_work);
		cloneResume->m_name = this->m_name;
		cloneResume->m_sex = this->m_sex;
		cloneResume->m_age = this->m_age;
		return cloneResume;
	}
};
int main()
{
	Resume *a = new Resume;
	a->SetPersonalInfo("大鸟", "男", "29");
	a->SetWorkExperience("1998-2000", "XX公司");
	a->Display();


	Resume *b = (Resume*)a->Clone();
	b->SetPersonalInfo("黄成涛", "男", "22");
	b->SetWorkExperience("1997-2006", "YY公司");
	b->Display();

	Resume *c = (Resume*)a->Clone();
	c->SetPersonalInfo("小菜", "男", "20");
	c->SetWorkExperience("2000-2006", "ZZ公司");
	c->Display();

	delete a;
	delete b;
	delete c;
	a = b = c = nullptr;
	system("pause");
	return 0;
}

运行结果为:

原型模式涉及到浅拷贝 和 深拷贝的问题, 但是浅拷贝对原型模式来说意义不大,因为浅拷贝复制的对象都一样。所以说上述代码用的是深拷贝。

 

其实原型模式算是一种十分简单的设计模式,因此用途也十分广泛。一般是与其他设计模式结合起来使用,例如,与工厂模式结合使用,内部使用 clone() 方法,然后由工厂方法提供给调用者使用。此外,原型模式在创建大量对象时,特别适用。

猜你喜欢

转载自blog.csdn.net/qq_34536551/article/details/88998112
今日推荐