设计模式---原型模式

90 阅读4分钟

原型模式的定义

原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制现有对象来创建新对象,而不是通过实例化类来创建新的对象。这种模式特别适用于需要大量类似对象的场景,可以提高系统的性能,降低创建对象的复杂性。

核心思想

原型模式的核心思想是:通过给出一个原型对象,并通过调用该原型对象的clone方法来创建一个新的对象。这个新对象是原型对象的一个克隆(浅复制或深复制),并且可以独立于原型对象进行修改。

原型模式的组成

  • 原型接口(Prototype Interface) :定义一个clone方法,用于克隆对象。

  • 具体原型类(Concrete Prototype Class) :实现原型接口,具体实现clone方法,返回对象的副本。

  • 客户端(Client) :使用原型实例创建新对象。

优点

  • 减少子类的创建:通过克隆现有对象,可以避免创建大量子类。

  • 提高性能:克隆对象比通过实例化类创建新对象更高效,尤其是在创建复杂对象时。

  • 简化对象的创建过程:通过复制已有对象,可以简化复杂对象的创建过程。

缺点

  • 深拷贝和浅拷贝问题:在实现深拷贝时,需要特别注意对象中引用类型的数据的复制问题。

  • 克隆方法的实现复杂度:对于包含复杂引用关系的对象,实现克隆方法可能会比较复杂。

代码示例

1、工作经历类

这个类包含了简历中的工作经历信息。包含公司名称、职位和工作时长。提供了一个拷贝构造函数用于深拷贝。

// 工作经历类
class WorkExperience
{
public:
	WorkExperience(const std::string& company, const std::string& position, const std::string duration)
		: company(company), position(position), duration(duration) {}

	// 拷贝构造函数
	WorkExperience(const WorkExperience& other)
		: company(other.company), position(other.position), duration(other.duration) {}

	void print() const
	{
		std::cout << "Company: " << company << ", Position: " << position << ", Duration: " << duration << std::endl;
	
	}

private:
	std::string company;
	std::string position;
	std::string duration;
};

2、原型接口

这个类定义了一个原型接口,包含克隆方法和打印方法。

// 原型接口
class Prototype
{
public:
	virtual ~Prototype() = default;
	virtual std::unique_ptr<Prototype> clone() const = 0;
	virtual void print() const = 0;
};

3、简历类

简历类继承自原型接口,实现了克隆方法和打印方法。

// 简历类
class Resume :public Prototype
{
private:
	std::string name;
	int age;
	std::vector<WorkExperience> workExperiences;

public:
	Resume(const std::string& name, int age) :name(name), age(age) {}

	void addWorkExperience(const std::string& company, const std::string& position, const std::string& duration)
	{
		workExperiences.emplace_back(company, position, duration);
	}

	std::unique_ptr<Prototype> clone() const override
	{
		// 深拷贝,创建一个新Resume对象,并复制所有成员变量
		std::unique_ptr<Resume> newResume = std::make_unique<Resume>(name, age);
		// 这里隐含了WorkExperience的拷贝构造函数调用
		newResume->workExperiences = workExperiences;	
		return newResume;
	}

	void setName(const std::string& newName)
	{
		name = newName;
	}

	void setAge(int newAge)
	{
		age = newAge;
	}

	void print() const override
	{
		std::cout << "Name: " << name << ", Age: " << age << std::endl;
		for (const auto& we : workExperiences) 
		{
			we.print();
		}
	}
};

4、客户端代码

在客户端代码中,我们创建了一个简历对象,并添加了一些工作经历。然后克隆这个简历对象,并修改克隆对象的部分信息,最后打印出原型简历和克隆简历的信息。

// 客户端代码
int main()
{
	// 创建原型简历对象
	std::unique_ptr<Resume> originalResume = std::make_unique<Resume>("小明", 25);
	originalResume->addWorkExperience("Company A", "Developer", "2010-2015");
	originalResume->addWorkExperience("Company B", "Senior Developer", "2015-2020");

	// 克隆简历对象
	std::unique_ptr<Resume> clonedResume = std::unique_ptr<Resume>(static_cast<Resume*>(originalResume->clone().release()));

	// 修改克隆对象的信息
	clonedResume->setName("小红");
	clonedResume->setAge(28);
	clonedResume->addWorkExperience("Company C", "Lead Developer", "2020-Present");
	
	// 打印原型简历和克隆简历的信息
	std::cout << "Original Resume:" << std::endl;
	originalResume->print();

	std::cout << "\nCloned Resume:" << std::endl;
	clonedResume->print();

	system("pause");
	return 0;
}

std::unique_ptr<Resume> clonedResume = std::unique_ptr<Resume>(static_cast<Resume*>(originalResume->clone().release()));所做的工作:

  • 调用 clone 方法:

originalResume->clone(), 这会调用 Resume 类的 clone 方法,返回一个 std::unique_ptr<Prototype> 对象,其中包含一个 Resume 对象的副本。

  • 调用 release 方法:

originalResume->clone().release()release 方法会放弃 std::unique_ptr 对所管理对象的所有权,并返回一个原始指针(Prototype*)。这意味着返回的指针指向了之前由 std::unique_ptr 管理的对象,但该对象现在需要手动管理其生命周期。

  • 类型转换:

static_cast<Resume*>(originalResume->clone().release()),使用 static_castPrototype* 强制转换为 Resume*。这在逻辑上是安全的,因为我们知道 clone 方法返回的实际对象类型是 Resume

  • 创建新的 std::unique_ptr:

std::unique_ptr<Resume>(static_cast<Resume*>(originalResume->clone().release())),创建一个新的 std::unique_ptr<Resume>,并将之前的原始指针(Resume*)交给它管理。这确保了新对象在离开作用域时会被自动释放,避免内存泄漏。