【设计模式】使用原型模式完成业务中“各种O”的转换
文章目录
- 1.原型模式概述
- 2.浅拷贝与深拷贝
- 2.1.浅拷贝的实现方式
- 2.2.深拷贝的实现方式
- 3.结语
1.原型模式概述
原型模式是一种非常简单易懂的模型,在书上的定义是这样的:
Specify the kinds of objects to create using a prototypical instance,and create new objects by
copying this prototype.
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
通俗的讲,就是有一个现成的对象,我们通过复制或拷贝这个对象的方式来创建一个新的对象,这就是原型模式。
那么,我们为什么需要通过拷贝来创建对象呢?
我认为主要体现在两个维度:程序运行效率和开发效率
- 程序运行效率:如果对象的创建和初始化的代价比较大,创建比较繁琐,例如需要从数据库、RPC、网络等获取一些数据才能完成创建,这时候使用原型模式拷贝出一个对象无疑是效率更高的做法。
- 开发效率:我们在业务开发中,会涉及到不同的分层,每个分层都有自己的数据实例,例如与前端交互的
VO
对象,数据传输的DTO
对象,与数据库交互的PO
对象等等,这些对象转换的过程中大部分的字段值都是相同的,如果每一层都使用getter/setter
方法来进行赋值,开发效率就很容易受到影响,尤其是在迭代中增加了新字段的情况下,每一层都需要新增一个字段set
方法,很麻烦也很容易做漏,这时候使用原型模式来进行复制就比较方便了。
严格的说,原型模式是同一个类型下的不同实例的数据拷贝,对于不同类型的实例拷贝更应该归类于原型模式的一种拓展用法。不过在业务中,这种拓展用法使用的频率更高,接下来的示例也是以这种拓展用法为主。
2.浅拷贝与深拷贝
想要在使用原型模式的时候,不出现一些“意外”,那就得先了解浅拷贝与深拷贝之间的区别。
两者的区分非常好理解,关键点就是在对引用类型的成员变量拷贝上,两种拷贝类型有不同的结果:
- 浅拷贝:只会将原始对象中的引用类型变量的内存地址复制给目标对象的同名成员变量。
- 深拷贝:会以引用类型变量的类型为基础,创建一个崭新的对象,再把这个新对象的内存地址赋值给目标对象的对应同名成员变量。
对于浅拷贝来说,如果原始对象中有其他的引用类型变量,在拷贝出目标对象后,两个对象对于该引用类型变量的修改会互相影响,但是由于不需要针对引用类型的变量查询新的对象,这种拷贝方式的效率较高。在不会修改变量值或者只需要修改基本类型的变量值(包含String)时,优先考虑使用浅拷贝。
如果拷贝的对象中包含了引用类型的对象,且需要进行修改,或者拷贝不同类型但内部字段相同的对象(例如:UserPO和UserDTO),可以考虑使用深拷贝。
2.1.浅拷贝的实现方式
浅拷贝的实现方式有很多,归类起来主要是两种,一种是使用Java原生的方式,另一种是通过开源的工具包实现。
先看看Java原生的方式,实现一个Cloneable
接口,并覆写父类Object
中的clone
方法:
@Getter
@Setter
public class User implements Cloneable {private String name;private int age;private Phone phone;@Overridepublic User clone() throws CloneNotSupportedException {return (User) super.clone();}
}@Getter
@Setter
public class Phone {private String phoneNumber;
}
为了验证浅拷贝,这里还加入了一个Phone
对象,接下来就做个测试。
public static void main(String[] args) throws CloneNotSupportedException {User user = new User();user.setName("张三");user.setAge(18);user.setPhone(new Phone());User cloneUser = user.clone();System.out.println(user.getPhone() == cloneUser.getPhone());
}
打上一个断点,查看两个对象,可以看到User
对象已经成功复制,并且里面的Phone
对象明显是同一个对象,
使用开源的工具,常用的有两种工具分别是Apache commons
中的BeanUtils
或PropertyUtils
,Spring
中的BeanUtils
,他们都有一个共同的方法是copyProperties
用来拷贝对象属性。在实现中,虽然Apache
和Spring
都是通过反射来实现的,但是Spring
针对反射做了一层缓存,在相同类型的对象复制中,效率高于Apache
,所以我们选择使用Spring
的拷贝。
没有Spring
依赖的话,需要引入依赖包:
<dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.9</version>
</dependency>
修改一下代码,可以得到一样的结果:
public static void main(String[] args) {User user = new User();user.setName("张三");user.setAge(18);user.setPhone(new Phone());User cloneUser = new User();BeanUtils.copyProperties(user, cloneUser);System.out.println(user.getPhone() == cloneUser.getPhone());
}
2.2.深拷贝的实现方式
深拷贝的实现有两种方式,一种是通过递归来拷贝,既然一次拷贝对于引用变量来说只能拷贝地址,那就再把引用变量也做一次拷贝就可以了,只是这种方式太麻烦了。我们一般会选择第二种方式,通过序列化与反序列化来完成对象的拷贝。
其实在我们常见的RPC
通信中,就是使用的这种方式来完成对象拷贝的,请求方将对象序列化成流、字节数组、JSON、XML等等方式来做传输,接收方收到数据后,用同样的方式反序列化成一个新的对象。我们也可以采用这种方式来完成对象的拷贝。
这里使用一个FastJson
工具类做了一个简单的封装,封装了两个方法,拷贝单个对象和拷贝List对象:
public class DeepCloneUtil {/*** 克隆单个对象** @param source 被克隆的源对象* @param targetClazz 克隆目标对象的类型* @param <T> 目标对象泛型* @return 克隆模板对象*/public static <T> T cloneObject(Object source, Class<T> targetClazz) {String jsonString = JSON.toJSONString(source);return JSON.parseObject(jsonString, targetClazz);}/*** 克隆List对象** @param source 被克隆的源对象* @param targetClazz 克隆目标对象的类型* @param <T> 目标对象泛型* @return 克隆模板对象*/public static <T> List<T> cloneList(List<?> source, Class<T> targetClazz) {String jsonString = JSON.toJSONString(source);return JSON.parseArray(jsonString, targetClazz);}}
修改一下测试代码:
public static void main(String[] args) {User user = new User();user.setName("张三");user.setAge(18);user.setPhone(new Phone());User cloneUser = DeepCloneUtil.cloneObject(user, User.class);System.out.println(user.getPhone() == cloneUser.getPhone());
}
此时,两个Phone
对象就不是同一个对象了,这样就完成了深拷贝。
再试试列表拷贝:
public static void main(String[] args) {User user = new User();user.setName("张三");user.setAge(18);user.setPhone(new Phone());User user1 = new User();user1.setName("李四");user1.setAge(19);user1.setPhone(new Phone());List<User> list = Arrays.asList(user, user1);List<User> cloneList = DeepCloneUtil.cloneList(list, User.class);}
可以看到不管是List,还是User,还是Phone,每一个都是不同的对象。
3.结语
本篇主要讲述的是原型模式的概念及其使用,并引出了深拷贝与浅拷贝的区别。
不管是从创建对象的性能上考虑,还是从开发效率上考虑,都可以在合适的时候选择使用原型模式拷贝对象的方式来替代从头开始创建一个新的对象。
相关文章:

【设计模式】使用原型模式完成业务中“各种O”的转换
文章目录 1.原型模式概述2.浅拷贝与深拷贝2.1.浅拷贝的实现方式2.2.深拷贝的实现方式 3.结语 1.原型模式概述 原型模式是一种非常简单易懂的模型,在书上的定义是这样的: Specify the kinds of objects to create using a prototypical instance,and cre…...

[C++ 网络协议] IOCP(Input Output Completion Port)
1.什么是IOCP IOCP(Input Output Completion Port)输入输出完成端口。其实就是基于重叠I/O的一种改进的模型。 重叠I/O具有缺点:重复调用非阻塞模式的accpet函数和以进入alertablewait状态为目的的SleepEx函数会影响程序性能。 而IOCP提供…...

R实现地图相关图形绘制
大家好,我是带我去滑雪! 地图相关图形绘制具有许多优点,这些优点使其在各种领域和应用中非常有用。例如:地图相关图形提供了一种直观的方式来可视化数据,使数据更容易理解和分析。通过地图,可以看到数据的空…...

【Jmeter】性能测试脚本开发——性能测试环境准备、Jmeter脚本编写和执行
文章目录 一、常用的Jmeter元件二、性能测试环境准备三、编写Jmeter脚本四、执行测试脚本 一、常用的Jmeter元件 取样器-HTTP请求 作用:发送HTTP请求配置原件-HTTP请求默认值 作用:设置HTTP请求的默认参数配置原件-用户定义的变量 作用:定义…...

看好你家电视盒的后门!数千个Android电视盒感染了与欺诈相关的危险恶意软件
如果你从Android电视盒获得流媒体修复程序,则你的设备可能会被恶意软件所感染,这些恶意软件能够进行广告欺诈、创建假帐户,并通过悄悄地将你的数据转移到中国的服务器来销售对家庭网络的访问。 根据本周的一份新报告,网络安全公司…...

LeetCode 1251. 平均售价
题目链接:1251. 平均售价 题目描述 表:Prices Column NameTypeproduct_idintstart_datedateend_datedatepriceint (product_id,start_date,end_date) 是 prices 表的主键(具有唯一值的列的组合)。 price…...

TypeScript 笔记:String 字符串
1 对象属性 length 返回字符串的长度 2 对象方法 charAt() 返回在指定位置的字符 charCodeAt() 返回在指定的位置的字符的 Unicode 编码 concat 连接两个或更多的字符串 indexOf 返回某个指定的字符串值在字符串中首次出现的位置 lastIndexOf 从后向前搜索字符串&…...

蓝牙技术|Matter或能改变中国智能家居市场,蓝牙技术将得到进一步应用
近年来,智能家居开放协议标准Matter(目前版本 1.1)由连接标准联盟发布,该联盟是一个由数百家公司组成的全球性机构,旨在提供与物联网 (IoT) 相关的标准。例如,Matter 用于允许 Amazon Alexa、Apple Home、G…...

VB.NET vs. VB6.0:现代化编程语言 VS 经典老旧语言
目录 .NET背景: 特点: VB6.0背景: 特点: 两者之间的不同: 总结: 升华: .NET背景: VB.NET一种简单,现代,面向对象计算机编程语言,有微软开发,VB.NET是一种基于.NET Framework的面向对象…...

ViewPager、RecycleView实现轮播图
1.ViewPager实现轮播图形效果。 1)layout中,PageIndicatorView轮播的View <RelativeLayoutandroid:layout_width"match_parent"android:layout_height"200dp"android:orientation"vertical"><androidx.viewpager…...

【FreeRTOS】【STM32】01从零开始的freertos之旅 浏览源码下的文件夹
基于野火以及正点原子 在打开正点原子的资料pdf时,我遇到了pdf无法复制粘贴的问题,这里有个pdf解锁文字复制功能的网址,mark一下。超级pdf 参考资料《STM32F429FreeRTOS开发手册_V1.2》 官方资料 FreeRTOS 的源码和相应的官方书籍均可从官…...

【PPT】ppt里面使用svg图标
要想编辑好的PPT,少不了小图标的美化,图标可以使PPT变得更有趣,更易懂,更美观。 对于png,主要处理它的颜色,可使用【重新着色】功能。 对于jpg,主要处理它的背景,删除背景后同png处…...

uni-app:实现页面效果4(echarts数据可视化)
效果 代码 <template><view><view><view class"title">概况</view><view class"line_position"><view class"line1"><view class"item"><view class"one">今日销售…...

vue实现echarts中 9种 折线图图例
let datas [{ DivideScore: 7, UserScore: 7.2, Name: 目标制定 },{ DivideScore: 7, UserScore: 7, Name: 具体性 },{ DivideScore: 7, UserScore: 7.5, Name: 可衡量性 },{ DivideScore: 7, UserScore: 7, Name: 可实现性 },{ DivideScore: 7, UserScore: 7, Name: 时间限定…...

redis实战-实现用户签到UV统计
BitMap功能演示 我们针对签到功能完全可以通过mysql来完成,比如说以下这张表 用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为 1亿条 每签到一次需要使用(…...

作为创始人的价值观与心法,构建系统
价值观 绿色、健康、有趣、感恩、谦卑、责任、勇气、客观、冷静、自洽、尊重、价值、服务、善良、利他 作为co-founder衡量的一个很重要的标准就是这个人的人品,大家一起做事情的体验要好,才能有large energy,且流通。 乐观、通达…...

Go语言基础面经
1.go语言编程的好处是什么 编译和运行都很快。 在语言层级支持并行操作。 有垃圾处理器。 内置字符串和 maps。 函数是 go 语言的最基本编程单位。 2.说说go语言的select机制 select 机制用来处理异步 IO 问题 select 机制最大的一条限制就是每个 case 语句里必须是一个…...

服务器文件备份
服务器上,做好跟应用程序有关的文件备份(一般备份到远程的盘符),有助于当服务器发生硬件等故障时,可以对系统进行进行快速恢复。 下面以Windows服务器为例,记录如何做文件的备份操作。 具体操作如下&#…...

剑指offer——JZ68 二叉搜索树的最近公共祖先 解题思路与具体代码【C++】
一、题目描述与要求 二叉搜索树的最近公共祖先_牛客题霸_牛客网 (nowcoder.com) 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 1.对于该题的最近的公共祖先定义:对于有根树T的两个节点p、q,最近公共祖先LCA(T,p,q)表示一个节点x&#…...

[Spring] @Bean 修饰方法时如何注入参数
目录 一、Bean 的简单使用 1、正常情况 2、问题提出 二、解决方案 1、Qualifier 2、直接写方法名 三、特殊情况 1、DataSource 一、Bean 的简单使用 在开发中,基于 XML 文件配置 Bean 对象的做法非常繁琐且不好维护,因此绝大部分情况下都是使用…...

docker拉取镜像错误 missing signature key
您正在尝试使用 yum 在 CentOS 或 RHEL 系统上安装 docker-ce,但您遇到了一些问题。根据您提供的输出,这里有几个需要注意的点: 系统未注册: "This system is not registered with an entitlement server" 指示您的系统未注册。对于…...

基于可解释性特征矩阵与稀疏采样全局特征组合的人体行为识别
论文还未发表,不细说,欢迎讨论。 Title: A New Solution to Skeleton-Based Human Action Recognition via the combination usage of explainable feature extraction and sparse sampling global features. Abstract: With the development of deep …...

OpenCV4(C++)—— 仿射变换、透射变换和极坐标变换
文章目录 一、仿射变换1. getRotationMatrix2D()2. warpAffine() 二、透射变换三、极坐标变换 一、仿射变换 在OpenCV中没有专门用于图像旋转的函数,而是通过图像的仿射变换实现图像的旋转。实现图像的旋转首先需要确定旋转角度和旋转中心,之后确定旋转…...

http.header.Set()与Add()区别;
在Go语言中进行HTTP请求时,http.Header对象表示HTTP请求或响应的头部信息。http.Header是一个map[string][]string类型的结构,用于存储键值对,其中键表示HTTP头字段的名称,值是一个字符串切片,可以存储多个相同名称的头…...

vue-7-vuex
一、Vuex 概述 目标:明确Vuex是什么,应用场景以及优势 1.是什么 Vuex 是一个 Vue 的 状态管理工具,状态就是数据。 大白话:Vuex 是一个插件,可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。例如:购…...

SSO单点登录和OAuth2.0区别
一、概述 SSO是Single Sign On的缩写,OAuth是Open Authority的缩写,这两者都是使用令牌的方式来代替用户密码访问应用。流程上来说他们非常相似,但概念上又十分不同。SSO大家应该比较熟悉,它将登录认证和业务系统分离,…...

【轻松玩转MacOS】基本操作篇
引言 本文是系列的开篇,我将为大家介绍MacOS的基本操作。对于初次接触MacOS的用户来说,掌握这些基本操作是必不可少的。无论是启动和关机,还是使用键盘和鼠标,或者是快捷键的使用,这些基本操作都是你开始使用MacOS的第…...

华为ICT——第三章图像处理基本任务
目录 1:数字图像处理的层次:(处理-分析-理解)顺序不能错: 2:图像处理(图像处理过程): 3:图像分析(特征提取): 4&#x…...

(C++)引用的用法总结
引用(reference)是C极为重要的一部分,本文对其用法进行简单总结。 1. 引用的基本用法 引用的关键字为&,表示取地址的意思,引用变量定义如下: int m 1; int &n m; //定义 cout<<"n:…...

Charles:移动端抓包 / windows客户端 iOS手机 / 手机访问PC本地项目做调试
一、背景描述 1.1、本文需求:移动端进行抓包调试 1.2、理解Charles可以做什么 Charles是一款跨平台的网络代理软件,可以用于捕获和分析网络流量,对HTTP、HTTPS、HTTP/2等协议进行调试和监控。使用Charles可以帮助开发人员进行Web开发、调试…...