[01][01][05] 原型模式详解
2020-10-16 21:54
806 查看
1. 定义
指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
调用者不需要知道任何创建细节,不调用构造函数
2. 适用场景
- 类初始化消耗资源较多
- new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
- 构造函数比较复杂
- 循环体重生产大量对象时
- Spring 中的 scope 就是使用的原型模式
2.1 痛点问题场景
你一定遇到过大篇幅 getter,setter 赋值的场景.例如这样的代码
public void setParam(ExamPaperVo vo){ ExamPaper examPaper = new ExamPaper(); //试卷主键 examPaper.setExaminationPaperId(vo.getExaminationPaperId()); //剩余时间 curForm.setLeavTime(examPaper.getLeavTime()); //单位主键 curForm.setOrganizationId(examPaper.getOrganizationId()); //考试主键 curForm.setId(examPaper.getId()); //考场主键 curForm.setExamroomId(examPaper.getExamroomId()); //用户主键 curForm.setUserId(examPaper.getUserId()); //专业 curForm.setSpecialtyCode(examPaper.getSpecialtyCode()); //岗位 curForm.setPostionCode(examPaper.getPostionCode()); //等级 curForm.setGradeCode(examPaper.getGradeCode()); //考试开始时间 curForm.setExamStartTime(examPaper.getExamStartTime()); //考试结束时间 curForm.setExamEndTime(examPaper.getExamEndTime()); //单选题重要数量 curForm.setSingleSelectionImpCount(examPaper.getSingleSelectionImpCount()); //多选题重要数量 curForm.setMultiSelectionImpCount(examPaper.getMultiSelectionImpCount()); //判断题重要数量 curForm.setJudgementImpCount(examPaper.getJudgementImpCount()); //考试时间 curForm.setExamTime(examPaper.getExamTime()); //总分 curForm.setFullScore(examPaper.getFullScore()); //及格分 curForm.setPassScore(examPaper.getPassScore()); //学员姓名 curForm.setUserName(examPaper.getUserName()); //分数 curForm.setScore(examPaper.getScore()); //是否及格 curForm.setResult(examPaper.getResult()); curForm.setIsPassed(examPaper.getIsPassed()); //单选答对数量 curForm.setSingleOkCount(examPaper.getSingleOkCount()); //多选答对数量 curForm.setMultiOkCount(examPaper.getMultiOkCount()); //判断答对数量 curForm.setJudgementOkCount(examPaper.getJudgementOkCount()); //提交试卷 service.submit(examPaper); }
代码非常工整,命名非常规范,注释也写的很全面,大家觉得这样的代码优雅吗?我认为,这样的代码属于纯体力劳动.那么原型模式,能帮助我们解决这样的问题
3. 分类
- 浅克隆:指拷贝对象时仅仅 copy 对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象
- 深克隆:不仅 copy 对象本身,而且 copy 对象包含的引用指向的所有对象
3.1 浅克隆实现
原型 prototype 接口
public interface Prototype { Prototype clone();
}需要克隆类
@Data @AllArgsConstructor
@NoArgsConstructor
public class ConcretePrototypeA implements Prototype {
/**
* 年龄
*/
private int age;
/**
* 姓名
*/
private String name;
/**
* 兴趣爱好
*/
private List hobbies;
@Override
public Prototype clone() {
return new ConcretePrototypeA(this.age, this.name, this.hobbies);
}
}测试类
package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;
import java.util.ArrayList;
public class PrototypeTest {
public static void main(String[] args) { ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA(18, "test_name", new ArrayList()); ConcretePrototypeA copy = (ConcretePrototypeA) concretePrototypeA.clone(); System.out.println("原对象与克隆对象内存地址对比, 地址是否相同:" + (concretePrototypeA == copy)); System.out.println("原对象与克隆对象中引用类型内存地址对比, 地址是否相同:" + (concretePrototypeA.getHobbies() == copy.getHobbies())); }
}
运行结果 ![](https://huaweirookie.oss-cn-shenzhen.aliyuncs.com/20200715213356.jpg) 从测试结果看出 hobbies 的引用地址是相同的,意味着复制的不是值,而是引用的地址.这样的话,如果我们修改任意一个对象中的属性值,concretePrototype 和 concretePrototypeCone 的 hobbies 值都会改变.这就是我们常说的浅克隆.只是完整复制了值类型数据,没有赋值引用对象 * 浅克隆对于基本数据类型和 String 类型字段会重新申请内存复制数据,克隆对象会指向新的内存地址 * 浅克隆对于引用类型的字段不会重新申请内存,而是把字段的内存地址指向之前原对象字段的内存地址 ## 3.2 深克隆 定义一个孙悟空类,拔一根毫毛吹出千万个猴子,每个猴子都有属于自己的金箍棒 通过字节码实现深克隆 * 待克隆类 ```java @Data @AllArgsConstructor @NoArgsConstructor public class JinGuBang implements Serializable { private float height = 100; private float wide = 10; public void big() { this.height *= 2; this.wide *= 2; } public void small() { this.height /= 2; this.wide /= 2; } }
@Data public class QiTianDaSheng implements Cloneable, Serializable { private JinGuBang jinGuBang; private int height; private int weight; private Date birthday; public QiTianDaSheng() { this.birthday = new Date(); this.jinGuBang = new JinGuBang(); } @Override protected Object clone() { return this.deepClone(); } public Object deepClone() { ByteArrayOutputStream byteArrayOutputStream = null; ObjectOutputStream objectOutputStream = null; ByteArrayInputStream byteArrayInputStream = null; ObjectInputStream objectInputStream = null; // 内存中完成操作,对象读写,是通过字节码直接操作 // 序列化操作类似 try { byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); objectInputStream = new ObjectInputStream(byteArrayInputStream); // 完整的新的对象, new 出来一个新的对象 QiTianDaSheng copy = (QiTianDaSheng) objectInputStream.readObject(); copy.setBirthday(new Date()); return copy; } catch (Exception e) { e.printStackTrace(); return null; } finally { IOUtils.closeQuietly(objectInputStream); IOUtils.closeQuietly(byteArrayInputStream); IOUtils.closeQuietly(objectOutputStream); IOUtils.closeQuietly(byteArrayOutputStream); } } }
- 测试类
```java
package com.zhunongyun.toalibaba.designpatterns.prototype.deep;
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
QiTianDaSheng clone = (QiTianDaSheng) qiTianDaSheng.clone(); System.out.println("深克隆,对象中的引用类型字段 JinGuBang,内存地址是否相同:" + (qiTianDaSheng.getJinGuBang() == clone.getJinGuBang())); }
}
运行结果 ![b4d128ce939829eeeff142cf1514531f.png](evernotecid://0B7710CE-D342-4510-A5F9-81F7B62D33F2/wwwevernotecom/149352153/ENResource/p155) # 4. 克隆破坏单例模式 如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例.实际上防止克隆破坏单例解决思路非常简单,禁止深克隆便可.要么你我们的单例类不实现 Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可,具体代码如下 ```java @Override protected Object clone() throws CloneNotSupportedException { return INSTANCE; }
5. Cloneable 源码分析
Cloneable 是标记型的接口,它们内部都没有方法和属性,实现 Cloneable 来表示该对象能被克隆,能使用 Object.clone()方法
如果没有实现 Cloneable 的类对象调用 clone()就会抛出 CloneNotSupportedException
在 ArrayList 中存在 clone 方法,但属于浅克隆
public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }
相关文章推荐
- (二十三)原型模式详解(clone方法源码的简单剖析)
- 详解Java中的clone方法 -- 原型模式
- 详解Java中的clone方法 -- 原型模式
- Java常用的设计模式05:常用设计模式之原型模式(创建型模式)
- 详解Java中的clone方法 -- 原型模式
- (转)详解Java中的clone方法 -- 原型模式
- java IO体系之设计模式详解——01
- 详解Java中的clone方法 — 原型模式
- 详解Java中的clone方法 — 原型模式
- JAVA设计模式是个什么玩意儿_05_原型模式
- javascript原型模式用法实例详解
- 详解js产生对象的3种基本方式(工厂模式,构造函数模式,原型模式)
- 详解Java中的clone方法 -- 原型模式
- 设计模式之 原型模式详解(clone方法源码的简单剖析)
- 编程常用设计模式详解--(上篇)(工厂、单例、建造者、原型)
- 深入详解原型模式之深复制和浅复制
- Java描述设计模式(05):原型模式
- PHP设计模式之原型模式详解
- Github全面详解-05课后作业01
- JAVA设计模式-05-原型模式