设计模式(五)原型模式 Prototype
- 原型模式:
原型模式,是指基于一个已经给定的对象,通过拷贝的方式,创建一个新的对象,这个给定对象,就是“原型”。
在 Java 中,原型模式体现为 Object 的 clone() 方法。
所有类都可以通过实现 Cloneable 接口,以及重写 clone() 方法,来实现原型模式。
- 代码:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Liability implements Cloneable {private String code;
private String name;
private String category;
private boolean isMajor; @Override
protected Liability clone() throws CloneNotSupportedException {
return (Liability) super.clone();
}
}
@Data
@Builder
public class PolicyShallowClone implements Cloneable { private String code;
private int applicantAge;
private Liability liability;
private List<String> specialDescriptions; @Override
public PolicyShallowClone clone() throws CloneNotSupportedException {
return (PolicyShallowClone) super.clone();
}
}
- 缩减 clone() 方法的返回类型:
自 JDK1.5 开始,Java 引进了一个新的特性:协变返回类型(covariant return type)。
即:覆盖方法的返回类型,可以是被覆盖方法的返回类型的子类。
所以需要在 clone() 方法内部进行强转。
这体现了一条通则:永远不要让客户去做任何类库能够替客户完成的事情。
- clone() 是一种浅度复制(Shallow Copy):
@Test
void testPolicy1() throws Exception {
// Build original policy
Liability liability = new Liability.LiabilityBuilder().code("0001").name("Liability").category("XPXA").build();
String specialDescription1 = "text1";
String specialDescription2 = "text2";
List<String> specialDescriptions = new ArrayList<>(Arrays.asList(specialDescription1, specialDescription2));
PolicyShallowClone policyA = PolicyShallowClone.builder().specialDescriptions(specialDescriptions).liability(liability).code("code001").applicantAge(18).build();
// Call clone
PolicyShallowClone policyB = policyA.clone();
Assertions.assertSame(policyA.getCode(), policyB.getCode());
Assertions.assertEquals(policyA.getCode(), policyB.getCode());
// Assert shallow clone
policyA.getSpecialDescriptions().add("text3");
Assertions.assertSame(policyA.getLiability(), policyB.getLiability());
Assertions.assertTrue(policyA.getSpecialDescriptions().size() == policyB.getSpecialDescriptions().size());
}
- 编写一个优秀的 clone() 方法:
克隆对象的数据来源,必须来自于 clone() 方法,所以永远在方法内部调用 super.clone() 方法。
所有的父类必须很好地实现了 clone() 方法。
如果当前类包含的域引用了可变对象,需要递归地调用 clone() 方法。
如果在线程安全的类中实现 Cloneable 接口,clone() 方法必须得到很好的同步。
- 一个深度复制的 clone() 方法:
@Data
@Builder
public class PolicyDeepClone implements Cloneable { private String code;
private int applicantAge;
private Liability liability;
private List<String> specialDescriptions; @Override
public PolicyDeepClone clone() throws CloneNotSupportedException {
PolicyDeepClone clone = (PolicyDeepClone) super.clone();
clone.specialDescriptions = new ArrayList<>(this.specialDescriptions);
clone.liability = this.liability.clone();
return clone;
}
}
- 深度复制的测试:
@Test
void testPolicy2() throws Exception {
// Build original policy
Liability liability = new Liability.LiabilityBuilder().code("0001").name("Liability").category("XPXA").build();
String specialDescription1 = "text1";
String specialDescription2 = "text2";
List<String> specialDescriptions = new ArrayList<>(Arrays.asList(specialDescription1, specialDescription2));
PolicyDeepClone policyA = PolicyDeepClone.builder().specialDescriptions(specialDescriptions).liability(liability).code("code001").applicantAge(18).build();
// Call clone
PolicyDeepClone policyB = policyA.clone();
// Assert deep clone
policyA.getSpecialDescriptions().add("text3");
Assertions.assertNotSame(policyA.getLiability(), policyB.getLiability());
Assertions.assertFalse(policyA.getSpecialDescriptions().size() == policyB.getSpecialDescriptions().size());
}
- 有必要这么复杂吗?
从上述的介绍,我们不难发现,要完成一个优秀的 clone() 方法,存在诸多限制。
并且,当我们实现了 clone() 方法,在编译器中,还会看到一条 Blocker 级别的 Sonar 警告:
Remove this "clone" implementation; use a copy constructor or copy factory instead.
它推荐的是一个拷贝构造器和拷贝工厂。
- 拷贝构造器(Copy constructor)
@Data
@Builder
public final class PolicyCopyConstructor { private String code;
private int applicantAge;
private Liability liability;
private List<String> specialDescriptions; public PolicyCopyConstructor(PolicyCopyConstructor policy) {
this.code = policy.code;
this.applicantAge = policy.applicantAge;
this.liability = policy.liability;
this.specialDescriptions = policy.specialDescriptions;
}
}
显然,这是一个浅度复制的实现,如果需要深度复制,需要深一步挖掘,这里不详述。
- 拷贝工厂(Copy factory):
@Data
public final class PolicyCopyFactory { private String code;
private int applicantAge;
private Liability liability;
private List<String> specialDescriptions; public static PolicyCopyFactory newInstance(PolicyCopyFactory policy) {
PolicyCopyFactory copyPolicy = new PolicyCopyFactory();
copyPolicy.setCode(policy.getCode());
copyPolicy.setApplicantAge(policy.getApplicantAge());
copyPolicy.setLiability(policy.getLiability());
copyPolicy.setSpecialDescriptions(policy.getSpecialDescriptions());
return copyPolicy;
}
}
拷贝工厂本质上使我们之前提到过的静态工厂的一种变形。
在这里,这也是浅度复制的实现。
- Copy constructor & Copy factory 的优势:
- 不依赖于某一种带有风险的,语言之外的对象创建机制(clone 是 native 方法)。
- 不会与 final 域的正常使用发生冲突(clone 架构与引用可变对象的 final 域的正常使用是不兼容的)。
- 不会抛出受检异常。
- 不需要类型转换。
- 《Effective Java》 第11条:谨慎地覆盖 clone
鉴于 clone() 方法存在这么多限制,《Effective Java》明确指出:
除了拷贝数组,其他任何情况都不应该去覆盖 clone() 方法,也不该去调用它。
- 关于深复制:
这篇文章 第004弹:几种通用的深度复制的方法 介绍了几种深复制的通用方法。
设计模式(五)原型模式 Prototype的更多相关文章
- 设计模式五: 原型模式(Prototype)
简介 原型模式是属于创建型模式的一种,是通过拷贝原型对象来创建新的对象. 万能的Java超类Object提供了clone()方法来实现对象的拷贝. 可以在以下场景中使用原型模式: 构造函数创建对象成本 ...
- 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)
原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...
- 二十四种设计模式:原型模式(Prototype Pattern)
原型模式(Prototype Pattern) 介绍用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象.示例有一个Message实体类,现在要克隆它. MessageModel usin ...
- [设计模式] 4 原型模式 prototype
设计模式:可复用面向对象软件的基础>(DP)本文介绍原型模式和模板方法模式的实现.首先介绍原型模式,然后引出模板方法模式. DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创 ...
- 设计模式 笔记 原型模式 prototype
//---------------------------15/04/07---------------------------- //prototype 原型模式--对象创建型模式 /* 1:意图: ...
- python 设计模式之原型模式 Prototype Pattern
#引入 例子1: 孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来. 例子2:寄个快递下面是一个邮寄快递的场景:“给我寄个快递.”顾客说.“寄往什么地方?寄给……?”你问.“和上次差不多一样,只是邮 ...
- 【UE4 设计模式】原型模式 Prototype Pattern
概述 描述 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.如孙悟空猴毛分身.鸣人影之分身.剑光分化.无限剑制 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, ...
- 【设计模式】—— 原型模式Prototype
前言:[模式总览]——————————by xingoo 模式意图 由于有些时候,需要在运行时指定对象时哪个类的实例,此时用工厂模式就有些力不从心了.通过原型模式就可以通过拷贝函数clone一个原有的 ...
- 创建型设计模式之原型模式(Prototype)
结构 意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性 当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者 为了避免创建一个与产品类层次平行的工厂类层次时:或 ...
- 设计模式之原型模式(prototype)
原理:拷贝自身对象实际上就是调用的拷贝构造函数,注意事项是这里的拷贝是深拷贝,即需要拷贝指针所指的内容 #include <stdio.h> #include <memory> ...
随机推荐
- Erlang程序设计(第2版)读书笔记(一)
正如<代码的未来>中所说,为了充分利用多核,并发变成将成为未来发展的趋势,对于并发编程的支持,Erlang确实是不二之选,Erlang在国内仍然较为小众,经典书籍相对也要少很多,最终选择了 ...
- HDU 4741 Save Labman No.004 (几何)
题意:求空间两线的最短距离和最短线的交点 题解: 线性代数和空间几何,主要是用叉积,点积,几何. 知道两个方向向量s1,s2,求叉积可以得出他们的公共垂直向量,然后公共垂直向量gamma和两线上的点形 ...
- 监控服务端口状态python脚本
#!/usr/bin/python import socket,os,time data={ 8080:"tomcat9", 18080:"tomcat_hjgdmj&q ...
- KMP算法入门讲解
字符串匹配问题.假设文本是一个长度为$n$的字符串$T$,模板是一个长度为$m$的字符串$P$,且$m\leq n$.需要求出模板在文本中的所有匹配点$i$,即满足$T[i]=P[0],T[I+1]= ...
- 什么样子的WordPress网站更受搜索引擎欢迎
网站的导航功能对于搜索引擎而言是非常重要的 网站的导航功能对于帮助用户迅速找到他们想要的内容来说是很重要的.它对帮助搜索引擎理解该网站有哪些重要内容同样非常重要.虽然百度的搜索结果都是指向每一个特定的 ...
- CVE-2011-0065
环境 备注 操作系统 Windows 7 x86 sp1 专业版 漏洞软件 Firefox 版本号:3.6.16 调试器 Windbg 版本号:6.12.0002.633 0x00 漏洞描述 在F ...
- FMDB中的数据处理
[self.db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)" ...
- Pig Latin-freecodecamp算法题目
Pig Latin 1.要求 Pig Latin把一个英文单词的第一个辅音或辅音丛(consonant cluster)移到词尾,然后加上后缀 "ay". 如果单词以元音开始,你只 ...
- Seek and Destroy-freecodecamp算法题目
Seek and Destroy(摧毁数组) 要求 实现一个摧毁(destroyer)函数,第一个参数是待摧毁的数组,其余的参数是待摧毁的值. 思路 利用for循环将输入arguments中除第一项待 ...
- C/C++字符串笔试知识点及实例
一.C字符串与C++字符串的深入理解 对于C语言,需要区分C字符串和C字符数组. C字符串:以字符NULL('\0')结尾的字符数组: C字符数组:数组元素类型为字符类型. C字符串的的初始化:c ...