学习目标
- 掌握原型模式和建造者的应用场景
- 掌握原型模式的浅克隆和深克隆的写法
- 掌握建造者模式的基本写法
- 了解克隆是如何破坏单例的
- 了解原型模式的优缺点
- 掌握建造者模式和工厂模式的区别
原型模式的定义
- 原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创新的对象。
- 调用者不需要知道任何创建细节,不调用构造函数
- 属于创建型模式
原型模式的适用场景
- 类初始化消耗资源较多
- new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中生产大量对象时
原型模式Java JDK中的应用
- ArrayList
- HashMap
原型模式的优点
- 性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建过程,不需要通过构造方法或者get\set方法反复设置参数
原型模式的缺点
- 必须配备克隆(或者可拷贝)方法
- 当对已有类进行改造的时候,需要修改代码,违反了开闭原则
- 深拷贝、浅拷贝需要运用得当
- 原型模式和单例模式需求区分开使用(一个类不能是单例又是原型模式)
深克隆和浅克隆的区别
- 深克隆拷贝的是值
- 浅克隆拷贝的是引用
原型
- 例子 原型对象
import lombok.Data;
@Data
public class ExamPaper{
private String examinationPaperId;//试卷主键
private String leavTime;//剩余时间
private String organizationId;//单位主键
private String id;//考试主键
private String examRoomId;//考场主键
private String userId;//用户主键
private String specialtyCode;//专业代码
private String postionCode;//报考岗位
private String gradeCode;//报考等级
private String examStartTime;//考试开始时间
private String examEndTime;//考试结束时间
private String singleSelectionImpCount;//单选选题重要数量
private String multiSelectionImpCount;//多选题重要数量
private String judgementImpCount;//判断题重要数量
private String examTime;//考试时长
private String fullScore;//总分
private String passScore;//及格分
private String userName;//学员姓名
private String score;//考试得分
private String resut;//是否及格
private String singleOkCount;//单选题答对数量
private String multiOkCount;//多选题答对数量
private String judgementOkCount;//判断题答对数量
public ExamPaper copy(){
ExamPaper examPaper = new ExamPaper();
//剩余时间
examPaper.setLeavTime(this.getLeavTime());
//单位主键
examPaper.setOrganizationId(this.getOrganizationId());
//考试主键
examPaper.setId(this.getId());
//用户主键
examPaper.setUserId(this.getUserId());
//专业
examPaper.setSpecialtyCode(this.getSpecialtyCode());
//岗位
examPaper.setPostionCode(this.getPostionCode());
//等级
examPaper.setGradeCode(this.getGradeCode());
//考试开始时间
examPaper.setExamStartTime(this.getExamStartTime());
//考试结束时间
examPaper.setExamEndTime(this.getExamEndTime());
//单选题重要数量
examPaper.setSingleSelectionImpCount(this.getSingleSelectionImpCount());
//多选题重要数量
examPaper.setMultiSelectionImpCount(this.getMultiSelectionImpCount());
//判断题重要数量
examPaper.setJudgementImpCount(this.getJudgementImpCount());
//考试时间
examPaper.setExamTime(this.getExamTime());
//总分
examPaper.setFullScore(this.getFullScore());
//及格分
examPaper.setPassScore(this.getPassScore());
//学员姓名
examPaper.setUserName(this.getUserName());
//分数
examPaper.setScore(this.getScore());
//单选答对数量
examPaper.setSingleOkCount(this.getSingleOkCount());
//多选答对数量
examPaper.setMultiOkCount(this.getMultiOkCount());
//判断答对数量
examPaper.setJudgementOkCount(this.getJudgementOkCount());
return examPaper;
}
@Override
public String toString() {
return "ExamPaper{" +
"examinationPaperId='" + examinationPaperId + '\'' +
", leavTime='" + leavTime + '\'' +
", organizationId='" + organizationId + '\'' +
", id='" + id + '\'' +
", examRoomId='" + examRoomId + '\'' +
", userId='" + userId + '\'' +
", specialtyCode='" + specialtyCode + '\'' +
", postionCode='" + postionCode + '\'' +
", gradeCode='" + gradeCode + '\'' +
", examStartTime='" + examStartTime + '\'' +
", examEndTime='" + examEndTime + '\'' +
", singleSelectionImpCount='" + singleSelectionImpCount + '\'' +
", multiSelectionImpCount='" + multiSelectionImpCount + '\'' +
", judgementImpCount='" + judgementImpCount + '\'' +
", examTime='" + examTime + '\'' +
", fullScore='" + fullScore + '\'' +
", passScore='" + passScore + '\'' +
", userName='" + userName + '\'' +
", score='" + score + '\'' +
", resut='" + resut + '\'' +
", singleOkCount='" + singleOkCount + '\'' +
", multiOkCount='" + multiOkCount + '\'' +
", judgementOkCount='" + judgementOkCount + '\'' +
'}';
}
}
BeanUtils拷贝对象,因为属性很多,用反射赋值(代码简洁,代码量少)
import java.lang.reflect.Field;
public class BeanUtils {
public static Object copy(Object protorype) {
Class clazz = protorype.getClass();
Object returnValue = null;
try {
returnValue = clazz.newInstance();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
field.set(returnValue, field.get(protorype));
}
}catch (Exception e){
e.printStackTrace();
}
return returnValue;
}
}
客户端测试
public class Client {
public static void main(String[] args) {
// ExamPaper examPaper = new ExamPaper();
// System.out.println(examPaper);
ExamPaper examPaper = (ExamPaper)BeanUtils.copy(new ExamPaper());
System.out.println(examPaper);
}
}
一、浅克隆
- 结论:hobbies是引用数据类型,浅克隆,拷贝的是原型对象值,他们是同一个引用,并没有生成新的对象
@Data
public class ConcretePrototype implements Cloneable {
private int age;
private String name;
private List<String> hobbies;
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
public class Client {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAge(18);
prototype.setName("Lisa");
List<String> hobbies = new ArrayList<>();
hobbies.add("音乐");
hobbies.add("美术");
prototype.setHobbies(hobbies);
// 拷贝原型对象
ConcretePrototype cloneType = prototype.clone();
cloneType.getHobbies().add("篮球");
System.out.println("原型对象:" + prototype);
System.out.println("克隆对象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println("原型对象的爱好:" + prototype.getHobbies());
System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
}
二、深克隆
@Data
public class ConcretePrototype implements Cloneable,Serializable {
private int age;
private String name;
private List<String> hobbies;
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public ConcretePrototype 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);
return (ConcretePrototype) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAge(18);
prototype.setName("Lisa");
List<String> hobbies = new ArrayList<>();
hobbies.add("音乐");
hobbies.add("美术");
prototype.setHobbies(hobbies);
// 拷贝原型对象
ConcretePrototype cloneType = prototype.deepClone();
cloneType.getHobbies().add("篮球");
System.out.println("原型对象:" + prototype);
System.out.println("克隆对象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println("原型对象的爱好:" + prototype.getHobbies());
System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
}
执行结果:
单例
import lombok.Data;
import java.util.List;
@Data
public class ConcretePrototype implements Cloneable {
private int age;
private String name;
private List<String> hobbies;
private static ConcretePrototype instance = new ConcretePrototype();
private ConcretePrototype(){}
public static ConcretePrototype getInstance(){
return instance;
}
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = ConcretePrototype.getInstance();
prototype.setAge(18);
prototype.setName("Lisa");
List<String> hobbies = new ArrayList<>();
hobbies.add("音乐");
hobbies.add("美术");
prototype.setHobbies(hobbies);
// 拷贝原型对象
ConcretePrototype cloneType = prototype.clone();
cloneType.getHobbies().add("篮球");
System.out.println("原型对象:" + prototype);
System.out.println("克隆对象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println("原型对象的爱好:" + prototype.getHobbies());
System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
}
- 如果是单例模式,那么就不要实现Cloneable接口
- 如果要实现,clone方法如下
@Override
public ConcretePrototype clone() {
return instance;
}
- 原型跟单例模式是冲突的,只能二选一