前言
本文内容
- 介绍原型模式
- 实现原型模式
- 介绍原型模式的使用场景
正文
介绍
如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。 -设计模式之美
实现
实现方式很简单,就是实现 Cloneable , 然后重写下 clone 方法。实现 Cloneable 并没有什么必须要实现的方法,只是用于JVM判断该类可以使用 clone 方法。这种方式直接由 JVM 进行处理,将被拷贝对象的内存中存储的信息复制一份到新内存中,所以不会调用构造器
package cn.zjm404.stu.dp.creat.prototype;public class ProtoType implements Cloneable{//属性//方法@Overridepublic ProtoType clone() {ProtoType res = null;try{res = (ProtoType)super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return res;}}
浅拷贝与深拷贝
对于类中为引用数据类型的属性,浅拷贝只是把指向被拷贝的数据的地址复制一份进行保存,而深拷贝则是实例化一个同样的引用数据类型的对象,然后将被拷贝的对象的属性复制到新的对象中。
浅拷贝的好处就是拷贝比较快,但是对于还有引用数据类型属性的类,有一部分值是拷贝对象与背靠背对象共享的,如果这些值可以进行修改,那么就会出现问题。就像这样
package cn.zjm404.stu.dp.creat.prototype;import lombok.*;@Setter@Getter@AllArgsConstructor@NoArgsConstructor@ToStringpublic class MsgObject {private int id;private String msg;}
package cn.zjm404.stu.dp.creat.prototype;import lombok.Getter;import lombok.Setter;import lombok.ToString;@Setter@Getter@ToStringpublic class ProtoType implements Cloneable{private MsgObject msg1;@Overridepublic ProtoType clone() {ProtoType res = null;try{res = (ProtoType)super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return res;}}
package cn.zjm404.stu.dp.creat.prototype;public class Client {public static void main(String[] args) {ProtoType protoType = new ProtoType();protoType.setMsg1(new MsgObject(1,"hello world"));System.out.println("protoType: " + protoType.toString());ProtoType protoType1 = protoType.clone();protoType1.getMsg1().setMsg("HELLO WORLD");System.out.println("protoType1: " + protoType1.toString());System.out.println("protoType: " + protoType.toString());}}

可以看到被拷贝对象中的数据被修改了。解决方式有两种,一是对共享数据限制为读,不进行修改。二是使用深拷贝。
使用 Cloneable 来实现深拷贝
保证被拷贝中的引用数据类型属性都实现了 Cloneable ,然后重写被拷贝类的 clone ,将引用属性均使用 clone 方法
package cn.zjm404.stu.dp.creat.prototype;import lombok.*;@Setter@Getter@AllArgsConstructor@NoArgsConstructor@ToStringpublic class MsgObject implements Cloneable{private int id;private String msg;@Overridepublic MsgObject clone() throws CloneNotSupportedException {return (MsgObject) super.clone();}}
package cn.zjm404.stu.dp.creat.prototype;import lombok.Getter;import lombok.Setter;import lombok.ToString;@Setter@Getter@ToStringpublic class ProtoType implements Cloneable{private MsgObject msg1;@Overridepublic ProtoType clone() throws CloneNotSupportedException {ProtoType res = (ProtoType) super.clone();res.msg1 = res.msg1.clone();return res;}}
package cn.zjm404.stu.dp.creat.prototype;public class Client {public static void main(String[] args) throws CloneNotSupportedException {ProtoType protoType = new ProtoType();protoType.setMsg1(new MsgObject(1,"hello world"));System.out.println("protoType: " + protoType.toString());ProtoType protoType1 = protoType.clone();protoType1.getMsg1().setMsg("HELLO WORLD");System.out.println("protoType1: " + protoType1.toString());System.out.println("protoType: " + protoType.toString());}}
使用序列化实现深拷贝
核心方法如下,先将对象中的数据转为 byte 流,然后再进行读取,转为 protoType 返回。
public ProtoType deepClone() throws IOException, ClassNotFoundException {ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo = new ObjectOutputStream(bo);oo.writeObject(this);ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);return (ProtoType) oi.readObject();}
完整如下
package cn.zjm404.stu.dp.creat.prototype;import lombok.Getter;import lombok.Setter;import lombok.ToString;import java.io.*;@Setter@Getter@ToStringpublic class ProtoType implements Cloneable,Serializable{private MsgObject msg1;@Overridepublic ProtoType clone() throws CloneNotSupportedException {ProtoType res = (ProtoType) super.clone();res.msg1 = res.msg1.clone();return res;}public ProtoType deepClone() throws IOException, ClassNotFoundException {ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo = new ObjectOutputStream(bo);oo.writeObject(this);ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);return (ProtoType) oi.readObject();}}
package cn.zjm404.stu.dp.creat.prototype;
import lombok.*;
import java.io.Serializable;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class MsgObject implements Cloneable, Serializable {
private int id;
private String msg;
@Override
public MsgObject clone() throws CloneNotSupportedException {
return (MsgObject) super.clone();
}
}
package cn.zjm404.stu.dp.creat.prototype;
import java.io.IOException;
public class Client {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ProtoType protoType = new ProtoType();
protoType.setMsg1(new MsgObject(1,"hello world"));
System.out.println("protoType: " + protoType.toString());
ProtoType protoType1 = protoType.deepClone();
protoType1.getMsg1().setMsg("HELLO WORLD");
System.out.println("protoType1: " + protoType1.toString());
System.out.println("protoType: " + protoType.toString());
}
}
使用场景
如果对象中的数据来自数据库或来自网络时,当再创建新的对象时,可以使用拷贝模式,减少创建时间。

