前言(一些废话,可以忽略)
- 同样是创建型的设计模式,这种模式与工厂模式相比,结构更为简单,也更好理解,直接进入正题
- PS.部分类实现见文末
要解决的问题
- 克隆羊多利大家都知道,原型模式要解决的就是对象的复制问题,在没有原型模式的情况下,我们直接通过复制对象的属性值来获取新的相同对象
/**
* 羊
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class Sheep {
private String name;
private Integer age;
public Sheep(String name, Integer age) {
this.name = name;
this.age = age;
}
//...toString
//...setter
//...getter
}
- 在调用的时候直接通过获取对象属性,或设置相同的属性值来进行克隆,显然很原始,一旦原始对象发生变化,我们需要修改克隆使用的地方
/**
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class BaseClone {
public static void main(String[] args) {
Sheep sheep1 = new Sheep("Sheep1", 1);
//获取原对象属性
Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge());
//设置与原对象属性相同的值
Sheep sheep3 = new Sheep("Sheep1", 1);
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep1.hashCode());
System.out.println(sheep2.hashCode());
System.out.println(sheep3.hashCode());
}
}
浅拷贝原型模式
- “浅拷贝原型模式”,这个名字是我自己取的,如果雷同是巧合,反正原型模式的主要目的是对象的复制(克隆),与上一篇一样,我们同样不需要在意这些细节
- 浅拷贝,直接使用Object的clone方法,一个本地的复制方法,快速!高效!
- 实现一个CopyPrototype接口,自己写一个clone方法的接口方便深拷贝时重写clone方法,也可以直接实现Cloneable方法,然后调用父类clone实现拷贝
/**
* 可以克隆的羊
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class Sheep implements Cloneable, CopyPrototype{
private String name;
private Integer age;
@Override
public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
return (CopyPrototype) this.clone();
}
//@Override
//protected Object clone() throws CloneNotSupportedException {
// return super.clone();
//
//}
}
- 克隆方法的时候,直接调用接口的复制自我方法copyOnSelf
/**
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class ShallowPrototype {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep1 = new Sheep("sheep1", 2);
Sheep sheep2 = (Sheep) sheep1.copyOnSelf();
//值一样
System.out.println(sheep1);
System.out.println(sheep2);
//false,说明对象已经被复制
System.out.println(sheep1.hashCode() == sheep2.hashCode());
}
}
使用clone的深拷贝
- 存在一个问题,当我们的sheep还有一个引用对象作为成员变量的时候,如下
/**
* 羊
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class Sheep implements Cloneable, CopyPrototype {
private String name;
private Integer age;
private SheepProperties properties;
@Override
public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
Sheep sheep = (Sheep) this.clone();
return sheep;
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep1 = new Sheep("sheep1", 2);
SheepProperties properties = new SheepProperties("中国", "山羊");
sheep1.setProperties(properties);
Sheep sheep2 = (Sheep) sheep1.copyOnSelf();
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep1.hashCode());
System.out.println(sheep2.hashCode());
System.out.println(sheep1.getProperties().hashCode());
System.out.println(sheep2.getProperties().hashCode());
}
- 这时我们会发现,sheep1和sheep2中的属性properties的hashCode(如果没有重写,则是经由内存地址计算得出)会是一样的,这说明我们在克隆的时候没有复制引用对象,这就是一个问题,所以如果有引用对象,则需要将引用对象也实现Cloneable接口(我们自己的接口CopyPrototype可以不实现,与之前的逻辑一样)
@Override
public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
Sheep sheep = (Sheep) this.clone();
//深拷贝
sheep.setProperties((SheepProperties) properties.copyOnSelf());
return sheep;
}
class SheepProperties implements Cloneable, CopyPrototype {
private String placeOfProduction;
private String breeding;
@Override
public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
return (CopyPrototype) this.clone();
}
}
- 存在一个问题,如果我们需要再在羊Sheep类中增加新的属性呢,那么就需要在重写copyOnSelf方法,这就违反了ocp原则,如果我们的SheepProperties类中也要增加新的引用对象呢,又得重写,这样就无穷尽了,于是乎
基于序列化的深拷贝
- 我们使用流的方式将对象序列化,这样即使再对类做修改,或引用对象中还有引用对象,都无需去修改copyOnSelf方法,这样扩展起来就比较好了
/**
* 羊
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class Sheep implements Cloneable, CopyPrototype, Serializable {
private String name;
private Integer age;
private SheepProperties properties;
//...省略一些方法
@Override
public CopyPrototype copyOnSelf() throws Exception{
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(this);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(in);
Sheep sheep = (Sheep) objectInputStream.readObject();
return sheep;
}
}
- 属性SheepProperties 只需要实现序列化接口即可,如果属性中还有引用对象,当然也都需要实现序列化接口,就不需要实现copyOnSelf方法
class SheepProperties implements Serializable {
private String placeOfProduction;
private String breeding;
public SheepProperties(String placeOfProduction, String breeding) {
this.placeOfProduction = placeOfProduction;
this.breeding = breeding;
}
//...省略一些常规方法
}
- 最后还是通过测试方法,看一下克隆出来的对象的引用属性是否为同一个
/**
* @program: ade-someproblem
* @author: cade franklin
* @create: 2019-12-22 22:12
**/
public class DeepPrototype {
public static void main(String[] args) throws Exception {
Sheep sheep1 = new Sheep("sheep1", 2);
SheepProperties properties = new SheepProperties("中国", "山羊");
sheep1.setProperties(properties);
Sheep sheep2 = (Sheep) sheep1.copyOnSelf();
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep1.hashCode());
System.out.println(sheep2.hashCode());
System.out.println(sheep1.getProperties().hashCode());
System.out.println(sheep2.getProperties().hashCode());
}
}
- 属性的hashcode不是同一个,证实引用类型也被克隆了,但是我们在SheepProperties中并没有去写序列化相关代码,扩展性提高了,这是推荐使用的深拷贝,也是所谓的原型模式
总结
- 原型模式简单来说就是对象的复制,看了一些其他同学关于原型模式的写法,和我的不完全一样,还是那句话形不似,但神似,精神一脉相承,足矣!当然如果你有任何问题,欢迎私信我,我们一起讨论,嘻嘻😳
愿你不舍爱与自由。
来源:CSDN
作者:我不是警察
链接:https://blog.csdn.net/Jahnsonxi/article/details/103654433