文章目录
1. 原型模式
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,属于创建型模式。
原型模式的核心在于拷贝原型对象,以系统中已存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需再经历耗时对象初始化过程(不调用构造函数),性能提升许多,当对象的构建过程比较耗时时,可以利用当前系统中已存在的对象作为原型,对其进行克隆(一般是基于二进制流的复制)。躲避初始化过程,使得新对象的创建时间大大减少。
结构图如下所示:
从UML图中,我们可以看到,原型模式主要包含三个角色:
客户(Client):客户类提出创建对象的请求。
抽象原型(Prototype):规定拷贝借口。
具体原型(Concrete Prototype):被拷贝的对象。
2.原型模式的应用场景
1.大篇幅 getter, setter 赋值的场景。
2.类初始化消耗资源较多。
3.new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
4.构造函数比较复杂。
5.循环体中产生大量对象时。
在Spring中,原型模式应用得非常广泛,例如:我们经常使用的 JSON.parseObject() 也是一种原型模式。
3. 原型模式的通用写法
3.1 先创建 IProtoType 接口
public interface IProtoType<T> {
T clone();
}
3.2 创建具体需要克隆的类
@Data
public class ConcretePrototype implements Serializable, IProtoType {
private static final long serialVersionUID = -7318203310711047606L;
private String title;
private String orignalValue;
private Integer status;
private String thumbnail;
@Override
public ConcretePrototype clone() {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setTitle(this.title);
concretePrototype.setOrignalValue(this.orignalValue);
concretePrototype.setStatus(this.status);
concretePrototype.setThumbnail(this.thumbnail);
return concretePrototype;
}
@Override
public String toString() {
return "ConcretePrototype{" +
"title='" + title + '\'' +
", orignalValue='" + orignalValue + '\'' +
", status=" + status +
", thumbnail='" + thumbnail + '\'' +
'}';
}
}
3.3 测试代码:
public class ConcretePrototypeTest {
public static void main(String[] args) {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setTitle("图书");
concretePrototype.setStatus(1001);
System.out.println(concretePrototype);
ConcretePrototype clone = concretePrototype.clone();
System.out.println(clone);
}
}
3.3.1 运行结果:
原型模式就是这么简单,但是在实际开发过程中 JDK 已经帮我们实现了一个API, 我们只需要实现Cloneable接口即可,修改 ConcretePrototype 类:
直接运行,也会得到同样的结果。有了JDK的支持再多的属性复制我们也可以实现。
当我们新增一个属性的时候,我们会发现原型对象也发生了变化,这显然不符合我们的预期。因为我们希望克隆出来的对象应该和原型对象时两个独立的对象,不应该再有联系,应该是共用了一个内存地址,意味着复制的不是值,而是引用的地址。
浅克隆:
只是完整复制了值类型数据,没有赋值引用对象,换言之,所有的引用对象仍然指向原来的对象。
深克隆:
完整复制了值类型数据,并赋值引用对象,换言之,所有的引用对象仍然指向新的对象。
4. 使用序列化实现深度克隆
ConcretePrototype 类新增deepClone()方法。
public ConcretePrototype deepClone(){
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (ConcretePrototype)objectInputStream.readObject();
}catch (Exception e ){
e.printStackTrace();
return null;
}
}
运行程序,我们会发现我们得到我们期望的结果:
注: 克隆模式会破坏单例模式。如果我们克隆的目标的对象时单例对象,那意味着,深克隆就会破坏单例。
解决方案:禁止深克隆,不实现Cloneable接口,要么重新clone()方法。
5. 原型模式在源码中的应用
1.JDK中Cloneable接口。
2.ArrayList 类的实现,
6. 原型模式的优缺点
优点:
1,性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能提升了许多。
2,可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来简化了创建对象的过程,以便在需要的时候使用。
缺点:
1.需要为每一个类配置一个克隆方法。
2.克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
3.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦,因此,深拷贝,浅拷贝,需要谨慎使用。