设计模式:原型模式(Prototype)

设计模式:原型模式(Prototype)

  • 设计模式:原型模式(Prototype)
    • 模式动机
    • 模式定义
    • 模式结构
    • 时序图
    • 模式实现
    • 在单线程环境下的测试
    • 在多线程环境下的测试
    • 模式分析
    • 优缺点
    • 适用场景
    • 应用场景
    • 模式扩展
    • 应用实例
      • 实例 1:矩形对象克隆
      • 实例 2:图形原型注册表
    • 参考

设计模式:原型模式(Prototype)

原型模式(Prototype)属于创建型模式(Creational Pattern)的一种。

创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。

创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

模式动机

如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢? 首先, 你必须新建一个属于相同类的对象。 然后, 你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。

然而,并非所有对象都能通过这种方式进行复制, 因为有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的。

直接复制还有另外一个问题。 因为你必须知道对象所属的类才能创建复制品, 所以代码必须依赖该类。 即使你可以接受额外的依赖性, 那还有另外一个问题: 有时你只知道对象所实现的接口, 而不知道其所属的具体类, 比如可向方法的某个参数传入实现了某个接口的任何对象。

通过复制现有的实例来创建新的实例,无需知道相应类的信息。这就是原型模式(Prototype)的模式动机。

模式定义

原型模式(Prototype)属于创建型模式。

原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。

模式结构

原型模式(Prototype)包含如下角色:

  • 抽象原型类(Prototype):接口定义了一个抽象的克隆方法。
  • 具体原型类(Concrete Prototype):实现抽象原型类(接口)定义的克隆方法,提供一个具体的克隆方法来复制自己。
  • 客户端(Client):使用原型类的对象来实现具体的操作,即通过复制原型对象来创建新的对象。

在这里插入图片描述

时序图

略。

模式实现

抽象原型类 Prototype.h:

#ifndef _PROTOTYPE_H_
#define _PROTOTYPE_H_

class Prototype
{
public:
	virtual Prototype* clone() = 0;
};

#endif // !_PROTOTYPE_H_

具体原型类 ConcretePrototype1.h:

#ifndef _CONCRETE_PROTOTYPE_1_H_
#define _CONCRETE_PROTOTYPE_1_H_

#include "Prototype.h"

#include <string>

class ConcretePrototype1 : public Prototype
{
private:
	std::string m_strTypeName;

public:
	ConcretePrototype1(std::string name) : m_strTypeName(name) {};
	// 拷贝构造函数
	ConcretePrototype1(const ConcretePrototype1& rhs)
	{
		m_strTypeName = rhs.m_strTypeName;
	}

	Prototype* clone() override
	{
		return new ConcretePrototype1(*this);
	}
	std::string getTypeName() const
	{
		return m_strTypeName;
	}
};

#endif // !_CONCRETE_PROTOTYPE_1_H_

具体原型类 ConcretePrototype2.h:

#ifndef _CONCRETE_PROTOTYPE_2_H_
#define _CONCRETE_PROTOTYPE_2_H_

#include "Prototype.h"

#include <string>

class ConcretePrototype2 : public Prototype
{
private:
	std::string m_strTypeName;

public:
	ConcretePrototype2(std::string name) : m_strTypeName(name) {};
	// 拷贝构造函数
	ConcretePrototype2(const ConcretePrototype2& rhs)
	{
		m_strTypeName = rhs.m_strTypeName;
	}

	Prototype* clone() override
	{
		return new ConcretePrototype2(*this);
	}
	std::string getTypeName() const
	{
		return m_strTypeName;
	}
};

#endif // !_CONCRETE_PROTOTYPE_2_H_

在单线程环境下的测试

测试代码:

#include <iostream>
#include <stdlib.h>

#include "ConcretePrototype1.h"
#include "ConcretePrototype2.h"

using namespace std;

int main()
{
	ConcretePrototype1* p1 = new ConcretePrototype1("A");
	ConcretePrototype2* p2 = (ConcretePrototype2*)p1->clone();

	cout << p1->getTypeName() << endl;
	cout << p2->getTypeName() << endl;

	delete p1;
	delete p2;

	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

在多线程环境下的测试

略。

模式分析

  • 原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个克隆方法。
  • 所有的类对克隆方法的实现都非常相似。该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。 你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。
  • 支持克隆的对象即为原型。 当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造。

优缺点

优点:

  1. 可以克隆对象,而无需与它们所属的具体类相耦合。
  2. 可以克隆预生成原型,避免反复运行初始化代码,在创建大量对象时可以节省时间和资源,提高了对象创建的效率。
  3. 可以隐藏对象创建和初始化的复杂性,可以更方便地生成复杂对象,并且更容易管理和维护。
  4. 可以用继承以外的方式来处理复杂对象的不同配置。
  5. 可以在运行时动态添加和删除对象。
  6. 可以保护原始对象,防止意外修改对原对象产生影响。

缺点:

  1. 必须保证原始对象和克隆对象之间的区别,否则可能会产生副作用。
  2. 克隆包含循环引用的复杂对象可能会非常麻烦。
  3. 原型模式需要给对象添加一个克隆方法。但是,该方法可能不适用于所有对象类型,例如具有命令行参数的程序。

适用场景

在以下情况下可以使用原型模式:

  • 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
  • 如果子类的区别仅在于其对象的初始化方式,那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。

在原型模式中,你可以使用一系列预生成的、 各种类型的对象作为原型。客户端不必根据需求对子类进行实例化, 只需找到合适的原型并对其进行克隆即可。

应用场景

  1. 细胞有丝分裂:原始细胞就是一个原型,它在复制体的生成过程中起到了推动作用。
  2. 孙悟空的七十二变。
  3. Object类:Java中的所有类都直接或间接继承自Object类,它提供了一个clone()方法,允许对象在克隆时使用它们的原型对象。
  4. Spring框架:在Spring框架中,原型范围bean使用原型模式。例如,在Spring中,可以将作用域设置为prototype,来创建一个bean的多个独立实例,这样每次在容器中注入bean时,将创建新的实例。

模式扩展

原型注册表 (Prototype Registry)提供了一种访问常用原型的简单方法,其中存储了一系列可供随时复制的预生成对象。 最简单的注册表原型是一个 名称 → 原型 的哈希表。 但如果需要使用名称以外的条件进行搜索,你可以创建更加完善的注册表版本。

在这里插入图片描述

应用实例

实例 1:矩形对象克隆

公司正在开发一个图形设计软件,其中有一个常用的图形元素是矩形。设计师在工作时可能需要频繁地创建相似的矩形,而这些矩形的基本属性(如颜色、宽度、高度)相同,但具体的位置可能不同。为了提高设计师的工作效率,请你使用原型模式设计一个矩形对象的原型。该原型可以根据用户的需求进行克隆,生成新的矩形对象。

#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;

// 抽象原型类
class Prototype
{
public:
	virtual Prototype *clone() = 0;
};

// 具体的矩阵类
class RectanglePrototype : public Prototype
{
	// 成员属性
private:
	string m_color;
	int m_height;
	int m_width;
	int x_pos;
	int y_pos;

	// 成员函数
public:
	RectanglePrototype(string color, int height, int width) : m_color(color), m_height(height), m_width(width)
	{
		x_pos = 0;
		y_pos = 0;
	};

	// 重载克隆函数接口
	Prototype *clone() override
	{
		return new RectanglePrototype(*this);
	}
	// 信息打印
	void PrintInfo()
	{
		cout << "Color: " << m_color << ", Height: " << m_height << ", Width: " << m_width
			 << ", X pos: " << x_pos << ", Y pos: " << y_pos << endl;
	}
	void setPosition(int x, int y)
	{
		x_pos = x;
		y_pos = y;
	}
};

int main()
{
	string color = "Red";
	int height = 300, width = 100;
	// 创建原型对象
	RectanglePrototype *prototypeRectangle = new RectanglePrototype(color, height, width);
	prototypeRectangle->PrintInfo();
	// 克隆对象
	RectanglePrototype *clonedRectangle = (RectanglePrototype *)prototypeRectangle->clone();
	clonedRectangle->setPosition(50, 50);
	clonedRectangle->PrintInfo();

	delete clonedRectangle;
	clonedRectangle = nullptr;
	delete prototypeRectangle;
	prototypeRectangle = nullptr;

	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

实例 2:图形原型注册表

#include <iostream>
#include <string>
#include <unordered_map>
#include <stdlib.h>
using namespace std;

// 抽象原型类
class Prototype
{
public:
	virtual Prototype *clone() = 0;
	virtual void draw() = 0;
};

// 具体的矩阵类
class RectanglePrototype : public Prototype
{
	// 成员属性
private:
	string m_strTypeName;

	// 成员函数
public:
	RectanglePrototype(string name) : m_strTypeName(name){};

	// 重载克隆函数接口
	Prototype *clone() override
	{
		return new RectanglePrototype(*this);
	}
	void draw() override
	{
		cout << "Inside Rectangle::draw() method." << endl;
	}
	void setName(const string &name)
	{
		m_strTypeName = name;
	}
	string getName() const
	{
		return m_strTypeName;
	}
};

// 具体的正方形类
class SquarePrototype : public Prototype
{
	// 成员属性
private:
	string m_strTypeName;

	// 成员函数
public:
	SquarePrototype(string name) : m_strTypeName(name){};

	// 重载克隆函数接口
	Prototype *clone() override
	{
		return new SquarePrototype(*this);
	}
	void draw() override
	{
		cout << "Inside Square::draw() method." << endl;
	}
	void setName(const string &name)
	{
		m_strTypeName = name;
	}
	string getName() const
	{
		return m_strTypeName;
	}
};

// 具体的圆形类
class CirclePrototype : public Prototype
{
	// 成员属性
private:
	string m_strTypeName;

	// 成员函数
public:
	CirclePrototype(string name) : m_strTypeName(name){};

	// 重载克隆函数接口
	Prototype *clone() override
	{
		return new CirclePrototype(*this);
	}
	void draw() override
	{
		cout << "Inside Circle::draw() method." << endl;
	}
	void setName(const string &name)
	{
		m_strTypeName = name;
	}
	string getName() const
	{
		return m_strTypeName;
	}
};

class PrototypeRegistry
{
private:
	unordered_map<string, Prototype *> items;

public:
	void addItem(string id, Prototype *p)
	{
		items[id] = p;
	}
	Prototype *getById(string id)
	{
		return items[id]->clone();
	}
};

int main()
{
	RectanglePrototype *rect = new RectanglePrototype("Rectangle");
	SquarePrototype *square = new SquarePrototype("Square");
	CirclePrototype *circle = new CirclePrototype("Circle");
	PrototypeRegistry *prototypeRegistry = new PrototypeRegistry();

	prototypeRegistry->addItem("1", rect);
	prototypeRegistry->addItem("2", square);
	prototypeRegistry->addItem("3", circle);

	RectanglePrototype *clonedRect = (RectanglePrototype *)prototypeRegistry->getById("1");
	clonedRect->setName("my rectangle");
	cout << clonedRect->getName() << endl;
	clonedRect->draw();

	delete prototypeRegistry;
	delete rect;
	delete square;
	delete circle;
	delete clonedRect;

	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

参考

  1. https://design-patterns.readthedocs.io/zh-cn/latest/creational_patterns/creational.html
  2. https://www.runoob.com/design-pattern/prototype-pattern.html
  3. https://blog.csdn.net/weixin_45433817/article/details/131037102
  4. https://blog.csdn.net/weixin_45433817/article/details/131095164
  5. https://refactoringguru.cn/design-patterns/prototype
  6. https://www.cnblogs.com/ybqjymy/p/17534839.html
  7. https://www.jb51.net/article/55870.htm

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/651950.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【软件设计师】——6.程序设计语言与语言处理程序

目录 6.1基本概念 6.2编译与解释 6.3文法 6.4有限自动机 6.5正规式 6.6 表达式 6.7 传值与引用 6.8 数据类型与程序控制结构 6.9 程序语言特点 6.10 Java程序设计 6.11 C 6.12 python 6.1基本概念 语句&#xff1a;高级程序设计语言中描述程序的运算步骤、控制结构、…

hubilder Android模拟器华为手机连接不上

APP真机测试注意点&#xff1a; 1. 同一个局域网下 2. 手机连接USB模式&#xff08;华为选择USB配置&#xff1a;音频来源&#xff09; &#xff0c;开发者模式 3. 实在不行重启HBuilderX再运行真机 可是卡在了“正在安装手机端HBuilder调试基座...” 就没反应了&#xff1f;&…

【荐闻】空中目标检测综述

https://t.zsxq.com/tgUjbhttps://t.zsxq.com/tgUjb 这篇综述论文全面回顾了空中目标检测的最新进展&#xff0c;包括五个不平衡问题、相关方法、实际应用和性能评估。以下是对论文内容的详细描述&#xff1a; 1&#xff09;引言&#xff1a;介绍了空中目标检测的概念&#x…

【搭建大语言模型】使用LocalGPT搭建本地大语言模型服务并实现远程访问进行交互

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

【Go专家编程——内存管理——逃逸分析】

逃逸分析 逃逸分析&#xff08;Escape Analysis&#xff09;是指由编译器决定内存分配的位置&#xff0c;不需要程序员决定。 在函数中申请一个新的对象 如果分配在栈上&#xff0c;则函数执行结束后可自动将内存回收如果分配在堆上&#xff0c;则函数执行结束后可交给GC&…

clickhouse——clickhouse单节点部署及基础命令介绍

clickhouse支持运行在主流的64位CPU架构的linux操作系统之上&#xff0c;可以通过源码编译&#xff0c;预编译压缩包&#xff0c;docker镜像和rpm等多种方式进行安装。 一、单节点部署 1、安装curl工具 yum install -y curl 2、添加clickhouse的yum镜像 curl -s https://pack…

K-means聚类模型入门介绍

K-means聚类是一种无监督学习方法&#xff0c;广泛应用于数据挖掘、机器学习和模式识别等领域&#xff0c;用于将数据集划分为K个簇&#xff08;cluster&#xff09;&#xff0c;其中每个簇的数据具有相似的特征。其基本思想是通过迭代寻找使簇内点间距离平方和最小的簇划分方式…

【Django】从零开始学Django【2】

五. CBV视图 Django植入了视图类这一功能&#xff0c;该功能封装了视图开发常用的代码&#xff0c;无须编写大量代码即可快速完成数据视图的开发&#xff0c;这种以类的形式实现响应与请求处理称为CBV(Class Base Views)。 1. 数据显示视图 数据显示视图是将后台的数据展示…

[链表]求中间节点、反转链表、回文链表

一、求链表的中间节点 876. 链表的中间结点 - 力扣&#xff08;LeetCode&#xff09; 快慢指针法: 分别定义两个节点的指针(pSlow和pFast)指向链表的第一个节点&#xff0c;然后两个指针一起往后遍历链表&#xff0c;pFast一次移动两个节点&#xff0c;pSlow一次移动一个节点&…

基于Java的高校学生勤工助学优派系统的设计与实现(论文+源码)_kaic

摘 要 高校勤工助学管理系统的出现&#xff0c;让学生的工作更加标准,不仅仅使高校办公室的办公水平以及管理水平大大提高&#xff0c;还优化了勤工助学资金的使用方式方法&#xff0c;完善了资助所需费用的资源配置&#xff0c;可以卓有成效地缩减学校的管理经费。本系统主…

EPSON RX8111CE+松下高性能电池的组合应用

RTC是一种实时时钟&#xff0c;用于记录和跟踪时间&#xff0c;具有独立供电和时钟功能。在某些应用场景中&#xff0c;为了保证RTC在断电或者其他异常情况下依然能够正常工作&#xff0c;需要备份电池方案来提供稳定的供电。本文将介绍EPSON爱普生nA级RTC RX8111CE松下Panason…

Android 生成正式版密钥库 KeyStore

步骤1&#xff1a;打开生成正式版密钥库设置 点击 Build 菜单&#xff0c;选择 Generate Signed App Bundle or APK&#xff1a; 这是打开后的样子&#xff1a; 步骤2&#xff1a;选择 APK Android App Bundle 是用于上架 Google Play 商店的。 正常情况下选择 APK。 选择…

JVM学习-javap解析Class文件

解析字节码的作用 通过反编译生成字节码文件&#xff0c;可以深入了解Java工作机制&#xff0c;但自己分析类文件结构太麻烦&#xff0c;除了第三方的jclasslib工具外&#xff0c;官方提供了javapjavap是jdk自带的反解析工具&#xff0c;它的作用是根据class字节码文件&#x…

C语言实现Hash Map(3):Map代码优化

在上一节中&#xff0c;我们学习了C语言实现Hash Map(2)&#xff1a;Map代码实现详解&#xff0c;通过代码&#xff0c;我们更深入地了解了Map实现的原理&#xff0c;学习了如何通过key找到对应的桶并加入节点。也正如上一节提到的&#xff0c;虽然这是github中star比较多的代码…

You don‘t have enough free space或者no space left on device异常

1.磁盘空间不足 Linux安装软件显示 You dont have enough free space 或者docker拉镜像时&#xff0c;出现磁盘空间不足的情况 no space left on device 如果你是ubuntu系统。查看磁盘空间 df -h 多半是这个目录满了/dev/mapper/ubuntu--vg-ubuntu--lv 大多情况我们只希望扩…

Android 14 - 绘制体系 - VSync(1)

整体框架 VsyncConfiguration&#xff1a;一些基本参数的配置类&#xff0c;比如PhaseOffsets、WorkDuration等。 Scheduler&#xff1a;作为SF生成和派发VSync的整体调度器&#xff0c;主要面向SurfaceFlinger提供VSync相关接口。Scheduler包含对所有屏幕的VSync的控制。本身是…

影响所有股票、债券和ETF交易!一文看懂美国“T+1”结算新规

T1对投资者有何好处&#xff1f;有哪些风险&#xff1f;T1已经到来&#xff0c;T0还远吗&#xff1f; 美股将在本周迎来历史性时刻。 从当地时间5月28日开始&#xff0c;美股交易结算周期将由T2缩短至T1&#xff0c;即投资者当天卖出的股票&#xff0c;在交易后一个工作日就能…

[Python]pyenv 环境配置

。pip install pyenv安装 / 去Git 下载pyenv版本安装 。安装好后控制台输入pyenv查看版本 。pyenv install --list列出所有pyenv可以支持的python版本 。pyenv install 3.9.7 安装指定的版本 。在pycharm里&#xff0c;可以选中项目&#xff0c;点击File-Settings&#xff0…

如何评价 OpenAI 最新发布支持实时语音对话的模型GPT-4o?OpenAI发完GTP-4o,国内大模型行业还有哪些机会?

文章目录 OpenAI发完GTP-4o&#xff0c;国内大模型行业还有哪些机会&#xff1f;详细了解一下OpenAI最新发布的支持实时语音对话的模型GPT-4o国内大模型如何寻找发展机会&#xff1f;想要发展技术必须要创新与追赶或许应用场景拓展也是一种出路产业生态构建 ChatGPT 问世才 17 …

大字体学生出勤记录系统网页源码

源码介绍 上课需要一个个点名记录出勤情况&#xff0c;就借助AI制作了一个网页版学生出勤记录系统&#xff0c; 大字体显示学生姓名和照片&#xff0c;让坐在最后排学生也能看清楚&#xff0c;显示姓名同时会语音播报姓名&#xff0c; 操作很简单&#xff0c;先导入学生姓名…