设计模式:原型模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《访问者模式》 下一篇《享元模式》
简介:
原型模式,它是一种创建型设计模式,它允许通过复制原型对象来创建新的对象,而无需知道创建的细节。其工作原理是将一个原型对象传递给要创建的对象,然后通过请求原型对象复制自己来实施创建。
在原型模式中,克隆方法所创建的对象是全新对象,它们在内存中拥有全新的地址,通常对克隆所产生的对象进行修改不会对原型对象造成任何影响,每个克隆对象都是相互独立的。通过不同的方式对克隆对象进行修改后,可以得到一系列相似但不完全相同的对象。
需要注意的是,对原型对象的浅拷贝,对于数据类型是基本数据类型的成员变量,会直接进行值传递,也就是将该属性值复制一份给新的对象;对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
原型模式的使用场景:
1、类初始化需要消化非常多的资源,包括数据、硬件资源等。通过原型拷贝可以避免这些消耗。
2、通过使用new关键字创建一个对象需要非常繁琐的数据准备或访问权限。
3、一个对象需要提供给其他对象访问,而且各个调用者可能需要修改其值。在这种情况下,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
原型模式的创建步骤:
1、定义抽象原型类:抽象原型类是定义具有克隆自己的方法的接口,是所有具体原型类的公共父类。
2、定义具体原型类:具体原型类实现抽象原型类中的克隆方法,返回自己的一个克隆对象。
3、定义客户类:客户类让一个原型克隆自身,从而创建一个新的对象。在客户类中只需要直接实例化或通过工厂方法等创建一个对象,再通过调用该对象的克隆方法复制得到多个相同的对象。
原型模式通过复制原型对象来创建新对象,减少了创建新对象时所消耗的时间和资源。同时,由于复制的是原型对象,因此不会影响原对象的状态。
原型模式的优点,主要包括:
1、简化创建过程:原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
2、扩展性较好:由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
3、提供简化的创建结构:原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
4、支持深拷贝:原型模式可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
总的来说,原型模式可以大大提高创建对象的效率,同时还能保证系统的扩展性和灵活性。
原型模式的缺点,主要包括:
1、在实现深拷贝时可能需要比较复杂的代码,需要为每一个类配备一个克隆方法,而且该克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。
示例:
一、C#原型模式
以下是一个示例,展示了如何在C#中实现原型模式:
using System; namespace PrototypePatternExample
{ // 抽象原型类 public abstract class Prototype { public abstract Prototype Clone(); } // 具体原型类 public class ConcretePrototype : Prototype { private string _property; public ConcretePrototype(string property) { _property = property; } public override Prototype Clone() { // 使用深拷贝复制对象 ConcretePrototype clone = (ConcretePrototype)MemberwiseClone(this); return clone; } public void Display() { Console.WriteLine("Property: " + _property); } } class Program { static void Main(string[] args) { // 创建原型对象 ConcretePrototype prototype = new ConcretePrototype("Hello World"); // 克隆原型对象 ConcretePrototype clonePrototype = (ConcretePrototype)prototype.Clone(); // 显示原型对象和克隆对象的属性值是否相同 prototype.Display(); clonePrototype.Display(); Console.ReadLine(); } }
}
二、java原型模式
原型模式通常通过以下方式实现:
import java.util.ArrayList;
import java.util.List; abstract class Shape { private String name; public Shape(String name) { this.name = name; } public String getName() { return name; } public abstract void draw(); // 实现克隆方法 public Shape clone() { try { return (Shape) this.getClass().newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } }
} class Circle extends Shape { private int radius; public Circle(String name, int radius) { super(name); this.radius = radius; } @Override public void draw() { System.out.println("Drawing Circle"); }
} class Rectangle extends Shape { private int width; private int height; public Rectangle(String name, int width, int height) { super(name); this.width = width; this.height = height; } @Override public void draw() { System.out.println("Drawing Rectangle"); }
}public class PrototypePatternDemo { public static void main(String[] args) { List<Shape> shapeList = new ArrayList<>(); shapeList.add(new Circle("Circle 1", 5)); shapeList.add(new Rectangle("Rectangle 1", 5, 10)); shapeList.add(new Circle("Circle 2", 10)); shapeList.add(new Rectangle("Rectangle 2", 10, 20)); // 使用原型模式创建对象,节省创建对象的时间 for (Shape shape : shapeList) { Shape cloneShape = shape.clone(); System.out.println("Original Shape: " + shape.getName()); System.out.println("Clone Shape: " + cloneShape.getName()); } }
}
三、javascript原型模式
在JavaScript中,实现原型模式的关键是使用构造函数和原型对象。
下面是一个简单的JavaScript原型模式示例:
// 定义原型对象
var CarProto = { color: "blue", speed: 0, start: function() { console.log("Car started"); }, stop: function() { console.log("Car stopped"); }
}; // 定义构造函数
function Car(color) { this.color = color; this.speed = 0; // 将构造函数prototype属性指向原型对象 this.prototype = CarProto;
} // 定义子类
function SportCar() { // 调用父类构造函数 Car.call(this, "red"); // 重写父类方法 this.start = function() { console.log("SportCar started"); };
} // 设置原型对象,让SportCar继承CarProto
SportCar.prototype = CarProto;
在这个示例中,我们定义了一个CarProto原型对象,它包含了汽车的属性和方法。然后我们定义了一个Car构造函数,它接受颜色参数,并设置速度属性为0,并将它的prototype属性指向CarProto。这样,当我们创建一个新的汽车对象时,它就会继承CarProto的属性和方法。
然后我们定义了一个SportCar子类,它调用父类构造函数,并重写了父类的start方法。最后我们将SportCar.prototype设置为CarProto,这样SportCar就可以继承CarProto的属性和方法了。现在我们可以使用new关键字来创建SportCar对象了。
四、C++原型模式
以下是在C++中实现原型模式:
#include <iostream>
#include <string>
#include <map> using namespace std; // 定义原型接口
class Prototype {
public: virtual Prototype* clone() = 0; virtual void display() = 0;
}; // 具体原型类
class ConcretePrototype : public Prototype {
private: string name;
public: ConcretePrototype(string n) : name(n) {} void setName(string n) { name = n; } string getName() { return name; } // 实现克隆方法 Prototype* clone() { return new ConcretePrototype(*this); } // 实现显示方法 void display() { cout << "ConcretePrototype " << name << endl; }
}; // 工厂类
class PrototypeFactory {
private: map<string, Prototype*> prototypes; // 存储原型对象的映射表
public: PrototypeFactory() {} Prototype* create(string type) { // 创建原型对象 if (prototypes.find(type) == prototypes.end()) { // 如果该类型的原型对象不存在,则创建并存储在映射表中 prototypes[type] = new ConcretePrototype(type); } return prototypes[type]->clone(); // 返回克隆后的对象 }
}; int main() { PrototypeFactory factory; Prototype* p1 = factory.create("prototype1"); // 创建原型对象1的克隆对象1 p1->display(); // 显示原型对象1的名称,输出 "ConcretePrototype prototype1" Prototype* p2 = factory.create("prototype1"); // 创建原型对象1的克隆对象2 p2->setName("prototype2"); // 设置克隆对象2的名称,不影响原型对象1的名称 p2->display(); // 显示原型对象1的名称,输出 "ConcretePrototype prototype1",因为克隆对象2的名称没有修改成功,仍然是原型对象1的名称 delete p1; // 释放内存空间,因为p1和p2都是通过克隆得到的,所以应该释放内存空间,避免内存泄漏问题。 delete p2; // 释放内存空间,因为p1和p2都是通过克隆得到的,所以应该释放内存空间,避免内存泄漏问题。 return 0;
}
五、python原型模式
以下是在python中实现原型模式:
import copy # 定义原型类
class Prototype: def __init__(self, name): self.name = name def clone(self): return copy.deepcopy(self) # 定义具体原型类
class ConcretePrototype(Prototype): def __init__(self, name): super().__init__(name) self.data = [] def add_data(self, data): self.data.append(data) def clone(self): return ConcretePrototype(self.name) # 测试代码
if __name__ == '__main__': # 创建原型对象 prototype1 = ConcretePrototype("prototype1") prototype1.add_data(1) prototype1.add_data(2) print("Prototype 1 data:", prototype1.data) # 克隆原型对象 clone1 = prototype1.clone() clone1.add_data(3) print("Clone 1 data:", clone1.data) # [1, 2, 3] print("Prototype 1 data:", prototype1.data) # [1, 2] # 克隆克隆对象,避免修改原对象的影响 clone2 = clone1.clone() clone2.add_data(4) print("Clone 2 data:", clone2.data) # [1, 2, 4] print("Prototype 1 data:", prototype1.data) # [1, 2]
六、go原型模式
以下是一个示例,展示了如何在go中实现原型模式:
package main import ( "fmt"
) // 原型接口
type Prototype interface { Clone() Prototype
} // 具体原型类
type ConcretePrototype struct { Name string
} // 克隆方法实现原型接口
func (p *ConcretePrototype) Clone() Prototype { return &ConcretePrototype{Name: p.Name}
} func main() { // 创建原型对象 prototype1 := &ConcretePrototype{Name: "Prototype1"} // 克隆原型对象 clone1 := prototype1.Clone() fmt.Println("Clone 1 Name:", clone1.(*ConcretePrototype).Name) // 输出:Clone 1 Name: Prototype1 // 修改原型对象 prototype1.Name = "Prototype2" fmt.Println("Prototype 1 Name:", prototype1.(*ConcretePrototype).Name) // 输出:Prototype 1 Name: Prototype2 // 克隆克隆对象,避免修改原对象的影响 clone2 := clone1.Clone() fmt.Println("Clone 2 Name:", clone2.(*ConcretePrototype).Name) // 输出:Clone 2 Name: Prototype1
}
七、PHP原型模式
以下是一个示例,展示了如何在PHP中实现原型模式:
<?php class Prototype implements Cloneable { private $name; public function __construct($name) { $this->name = $name; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function clone() { return clone $this; }
} // 创建原型对象
$prototype = new Prototype("Original");
echo "Prototype Name: " . $prototype->getName() . "\n"; // 克隆原型对象
$clone = $prototype->clone();
echo "Clone Name: " . $clone->getName() . "\n"; // 修改原型对象的属性
$prototype->setName("Modified");
echo "Prototype Name after modification: " . $prototype->getName() . "\n"; // 克隆克隆对象,避免修改原对象的影响
$clone2 = $clone->clone();
echo "Clone 2 Name: " . $clone2->getName() . "\n";
《完结》
上一篇《访问者模式》 下一篇《享元模式》
相关文章:

设计模式:原型模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《访问者模式》 下一篇《享元模式》 简介: 原型模式,它是一种创建型设计模式,它允许通过复制原型对象来创建新的对象,而无需知道创建的细节。其工作原…...

SpringMVC 资源状态转移RESTful
文章目录 1、RESTful简介a>资源b>资源的表述c>状态转移 2、RESTful的实现HiddenHttpMethodFilterRESTful案例 1、RESTful简介 REST:Representational State Transfer,表现层资源状态转移。 a>资源 资源是一种看待服务器的方式,…...
verilog vscode linux
安装 vscode 插件 插件:Verilog-HDL/SystemVerilog/Bluespec SystemVerilog 功能:.xdc .ucf .v 等代码高亮、代码格式化、语法检查(Linting)、光标放到变量上提示变量的信息等 关于其他语言的依赖工具等信息查看插件说明 代码对齐…...

Postman日常操作
一.Postman介绍 1.1第一个简单的demo 路特斯(英国汽车品牌)_百度百科 (baidu.com) 1.2 cookie 用postman测试需要登录权限的接口时,会被拦截,解决办法就是每次请求接口前,先执行登录,然后记住cookie或者to…...

10月份程序员书单推荐
新书书单 1、C程序设计教程(第9版) 1.广受认可的《C程序设计教程》系列的第9版(个别版本也译作《C语言大学教程》),秉承了该系列一贯的丰富而详细的风格。该系列一些版本因封面画有蚂蚁形象而被称为“C语言蚂蚁书”。…...

【ChatGPT系列】ChatGPT:创新工具还是失业威胁?
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...
C++ 实现定时器的两种方法(线程定时和时间轮算法修改版)
定时器要求在固定的时间异步执行一个操作,比如boost库中的boost::asio::deadline_timer,以及MFC中的定时器。也可以利用c11的thread, mutex, condition_variable 来实现一个定时器。 1、使用C11中的thread, mutex, condition_variable来实现一个定时器。…...

2023mathorcup大数据竞赛选题建议及思路
大家好呀,昨天6点2023年第四届MathorCup高校数学建模挑战赛——大数据竞赛开赛,在这里给大家带来初步的选题建议及思路。 注意,本文章只是比较简略的图文讲解,更加详细完整的视频讲解请移步: 2023mathorcup大数据数学…...
部署vuepress项目到githubPage
部署vuepress项目到githubPage 1. 项目文件夹下有两个分支(main和gh-page) 1.1 main分支存放项目代码 1.2 gh-page分支存放 npm run docs:build之后的dist里面的所有文件 2. 分别提交到github上 3. 你的项目/docs/.vuepress/config.js module.export…...
ORACLE表空间说明及操作
ORACLE 表空间作用 数据存储:表空间是数据库中存储数据的逻辑结构。它提供了用于存储表、索引、视图、存储过程等数据库对象的空间。通过划分数据和索引等对象的存储,可以更好地管理和组织数据库的物理存储结构。性能管理和优化:通过将不同类…...

vue使用Element-plus的Image预览时样式崩乱
🔥博客主页: 破浪前进 🔖系列专栏: Vue、React、PHP ❤️感谢大家点赞👍收藏⭐评论✍️ 问题: 在使用组件库的image时出现了点小问题,预览的图片层级反而没有表格的层级高 效果图:…...

安装使用vcpkg的简易教程
目录 1. 首先安装vcpkg2. 在vcpkg目录下运行bootstrap-vcpkg.bat 命令3. 接着vs进行集成4. 使用vcpkg搜索可用的包5.下载安装所需包6.下载安装完成 1. 首先安装vcpkg 使用git命令下载 git clone https://github.com/Microsoft/vcpkg.git如果下载失败可直接下载文件 (vcpkg-ma…...

制作一个简单的C语言词法分析程序
1.分析组成 C语言的程序中,有很单词多符号和保留字。一些单词符号还有对应的左线性文法。所以我们需要先做出一个单词字符表,给出对应的识别码,然后跟据对应的表格来写出程序 2.程序设计 程序主要有循环判断构成。不需推理即可产生的符号我…...
Java项目中将MySQL改为8.0以上
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 maven依…...

软考高项-计算题(2)
题4 项目的总预算是包含管理储备的,所以总预算应该是:13238102*360 ETC(BAC-EV)/CPI BAC60 EV60*0.318 CPI18/200.9 ETC42/0.9 答案选择C A 题5 因为题目中提到了“按目前的状况继续发展”,那么是:ETC(BAC-EV)/CPI EV1230*0…...

Centos使用war文件部署jenkins
部署jenkins所需要的jdk环境如下: 这里下载官网最新的版本: 选择jenkins2.414.3版本,所以jdk环境最低得是java11 安装java11环境 这里直接安装open-jdk yum -y install java-11-openjdk.x86_64 java-11-openjdk-devel.x86_64下载jenkins最新…...

数据结构和算法——用C语言实现所有排序算法
文章目录 前言排序算法的基本概念内部排序插入排序直接插入排序折半插入排序希尔排序 交换排序冒泡排序快速排序 选择排序简单选择排序堆排序 归并排序基数排序 外部排序多路归并败者树置换——选择排序最佳归并树 前言 本文所有代码均在仓库中,这是一个完整的由纯…...

吃豆人C语言开发—Day2 需求分析 流程图 原型图
目录 需求分析 流程图 原型图 主菜单: 设置界面: 地图选择: 游戏界面: 收集完成提示: 游戏胜利界面: 游戏失败界面 死亡提示: 这个项目是我和朋友们一起开发的,在此声明一下…...

Nautilus Chain 联合香港数码港举办 BIG DEMO DAY活动,释放何信号?
在今年的 10 月 26 日 9:30-18:30 GMT8 期间,Nautilus Chain 联合香港数码港共同举办了 “BIG DEMO DAY” Web3 项目路演活动,包括Xwinner、Sleek、Tx、All weather、Coral Finance、DBOE、PARSIQ、Hookfi、Parallels、Fintestra 以及 dot.GAMING 等在内…...

手写RPC框架
文章目录 什么是RPC框架RPC框架中的关键点通信协议序列化协议动态代理和反射 目前已有的RPC框架手写RPC框架介绍项目框架项目执行流程项目启动 什么是RPC框架 RPC(Remote Procedure Call,远程过程调用), 简单来说遵循RPC协议的就是RPC框架. …...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...