设计模式:原型模式(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框架. …...
MCP4151数字电位器Arduino驱动与三线SPI时序详解
1. MCP4151 数字电位器 Arduino 库深度技术解析1.1 器件本质与工程定位MCP4151 是 Microchip 推出的单通道、10kΩ 标称阻值、257 抽头(0–256)非易失性数字电位器。其核心价值不在于替代模拟电位器进行手动调节,而在于为嵌入式系统提供可编程…...
一键迁移方案:OpenClaw配置备份与Qwen3-4B模型快速恢复
一键迁移方案:OpenClaw配置备份与Qwen3-4B模型快速恢复 1. 为什么需要配置迁移方案 上周我的开发机突然硬盘故障,导致所有OpenClaw配置丢失。重新配置飞书通道、模型连接和技能库花了大半天时间。这次教训让我意识到:自动化工具的配置本身也…...
Mac电脑免费小龙虾OpenClaw+Ollama使用心得
一、前言 很多人以为本地部署OpenClaw小龙虾(原始版)不管是调用国外大模型还是国内大模型,都要付费才能使用,并且如果是需要大耗量的token调用操作费用还不便宜。加上最近新闻发布的“龙虾”安全问题,因此很多人是望而…...
文心一言搜索优化,做好这件事就赢了一半
如果你在文心一言上铺了几百篇内容,但品牌词一问,AI还是引用别人——你缺的不是数量,是质量锚点。文心一言的算法有一套对“优质可信内容”的隐形成交系统,没通过质检的内容,发再多也是无效库存。去年我们实测过一个案…...
SpringBoot 自动配置原理与实践
核心机制解析SpringBoot 的自动配置基于条件化装配思想,通过 Conditional 系列注解实现动态加载。spring-boot-autoconfigure 模块包含大量预定义配置类,例如 DataSourceAutoConfiguration 在检测到类路径存在 HikariCP 时自动初始化数据源。关键组件包括…...
18.children 这个 props 的意义何在?该怎样正确使用?
在 React 里,children 是一个非常特殊、非常常用的 prop, 它专门用来接收:写在组件标签中间的那一部分内容。你可以把它理解为:组件外层负责搭“外壳”,children 负责装进这个壳里的“内容物”。一、children 到底是什…...
无障碍阅读助手:OpenClaw+Phi-3-vision-128k-instruct实时解析复杂图表
无障碍阅读助手:OpenClawPhi-3-vision-128k-instruct实时解析复杂图表 1. 为什么需要图表无障碍解析工具 去年帮一位视障朋友准备资格考试时,我深刻体会到技术文档中的图表是如何成为"信息黑洞"的。当他用屏幕阅读器听到"参见图3"…...
微前端状态管理的真相:Module Federation + 跨应用通信实战
本周大前端要闻Compose Multiplatform v1.11.10-alpha01:进一步完善跨平台 UI 状态同步能力,ViewModel 共享机制改进KotlinConf’26 演讲阵容公布:多场 Session 聚焦 Kotlin 多平台架构与状态管理,值得关注Retrofit 3.0.0 正式发布…...
算法分析与设计
欢迎来到我的软考中级——软件设计师备考合集。这里不只是一份简单的知识点堆砌,而是我在备考征途中,对庞杂知识体系进行深度梳理与内化的结晶。 面对浩瀚的考纲,从计算机组成原理的底层逻辑,到操作系统的进程调度;从数…...
拆解Clonezilla镜像:除了partclone,你还需要知道的底层原理与工具链
拆解Clonezilla镜像:从分卷压缩到文件系统的技术全景解析 当我们需要从Clonezilla备份中提取单个文件时,传统方法往往要求完整恢复整个镜像——这种"全有或全无"的方式在存储资源有限的情况下显得尤为笨重。本文将带您深入Clonezilla镜像的底层…...
