你一定遇到过大篇幅的getter、setter赋值的场景。例如这样的代码:
public void setParam(ExamPaperVo vo){
ExamPaper examPaper = new ExamPaper();
//试卷主键 examPaper.setExamination
PaperId(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);
}
代码工整,命名规范,注释也全面。但是这样的代码是纯体力劳动。原型模式能帮我们解决这样的问题。
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式主要使用于以下场景:
- 类初始化消耗资源较多。
- new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)。
- 构造函数比较复杂。
- 循环体内生产大量对象时候。
在Spring中,原型模式应用得非常广泛。例如scope="prototype",在我们经常用的JSON.parseObject()也是一种原型模式。下面,我们来看看原型模式类结构图:
一个标准的原型模式代码,应该是这样设计的。先创建原型prototype接口:
package com.wenbin.design.pattern.prototype;
public interface Prototype {
Prototype clone();
}
创建具体需要克隆的对象ConcretePrototype:
public class ConcretePrototypeA implements Prototype {
private int age;
private String name;
private List hobbies;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
public Prototype clone() {
ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA();
concretePrototypeA.setAge(this.age);
concretePrototypeA.setName(this.name);
concretePrototypeA.setHobbies(this.hobbies);
return concretePrototypeA;
}
}
创建Client对象:
public class Client {
private Prototype prototype;
public Client(Prototype prototype) {
this.prototype = prototype;
}
public Prototype startClone(Prototype concretePrototype) {
return concretePrototype.clone();
}
}
测试代码:
public class PrototypeTest {
public static void main(String[] args) {
// 创建一个具体的需要克隆的对象
ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA();
// 填充属性
concretePrototypeA.setAge(18);
concretePrototypeA.setName("prototypeA");
List<String> hobbies = new ArrayList<String>();
concretePrototypeA.setHobbies(hobbies);
System.out.println(concretePrototypeA);
// 创建Client对象准备开始克隆
Client client = new Client(concretePrototypeA);
ConcretePrototypeA concretePrototypeAClone = (ConcretePrototypeA) client.startClone(concretePrototypeA);
System.out.println(concretePrototypeAClone);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeAClone.getHobbies());
System.out.println("原对象中的引用类型地址值:" + concretePrototypeA.getHobbies());
System.out.println("对象地址比较:"+(concretePrototypeAClone.getHobbies() == concretePrototypeA.getHobbies()));
}
}
运行结果:
com.wenbin.design.pattern.prototype.ConcretePrototypeA@60e53b93
com.wenbin.design.pattern.prototype.ConcretePrototypeA@5e2de80c
克隆对象中的引用类型地址值:[]
原对象中的引用类型地址值:[]
对象地址比较:true
Process finished with exit code 0
从测试结果看出hobbies的引用地址的相同的,意味着复制的不是值,而是引用地址。这样的话,如果我们修改任意一个对象中的属性值,concretePrototypeA和concretePrototypeClone的hobbies值都会变。这就是我们常说的浅科通。只是完整复制了值类型数据,没有复制引用对象。换言之,所有的引用对象仍然指向原来的对象,嫣然不是我们想要的结果。下面我们来看看深度克隆。
我们换一个场景,大家都知道齐天大圣。首先它是一只猴子,有七十二般变化,拔一根毫毛就可以吹出千万个泼猴,手里还拿着金箍棒,金箍棒可以变大变小。这就是我们耳熟能详的原型模式的经典体现。
创建原型猴子Monkey类:
package com.wenbin.design.pattern.prototype.deep;
import java.util.Date;
public class Monkey {
public int height;
public int weight;
public Date birthday;
}
创建引用对象金箍棒类:
package com.wenbin.design.pattern.prototype.deep;
import java.io.Serializable;
public class JinGuBang implements Serializable {
public float h = 100;
public float d = 10;
public void big() {
this.d *= 2;
this.h *= 2;
}
public void small() {
this.d /= 2;
this.h /= 2;
}
}
创建具体的对象齐天大圣类:
package com.wenbin.design.pattern.prototype.deep;
import java.io.*;
import java.util.Date;
public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {
public JinGuBang jinGuBang;
public QiTianDaSheng() {
// 只是初始化
this.birthday = new Date();
this.jinGuBang = new JinGuBang();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
public Object deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
QiTianDaSheng copy = (QiTianDaSheng) ois.readObject();
copy.birthday = new Date();
return copy;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public QiTianDaSheng shallowClone(QiTianDaSheng target) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
qiTianDaSheng.height = target.height;
qiTianDaSheng.weight = target.height;
qiTianDaSheng.jinGuBang = target.jinGuBang;
qiTianDaSheng.birthday = new Date();
return qiTianDaSheng;
}
}
测试代码:
package com.wenbin.design.pattern.prototype.deep;
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
try {
QiTianDaSheng clone = (QiTianDaSheng) qiTianDaSheng.clone();
System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
} catch (Exception e) {
e.printStackTrace();
}
QiTianDaSheng q = new QiTianDaSheng();
QiTianDaSheng n = q.shallowClone(q);
System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
}
}
运行结果:
深克隆:false
浅克隆:true
Process finished with exit code 0
如果我们克隆目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止克隆破坏单例解决思路很简单,禁止深克隆即可。要么我们的单例类不实现Cloneable接口,要么我们重新clone()方法,在clone方法中返回单例对象即可。