基本介绍
- 原型模式指用原先实例指定创建对象的种类, 并且通过拷贝这些原型, 创建新的对象
- 原型模式是一种创建型设计模式, 允许一个对象再创建另一个可定制的对象, 无需指定如何创建的细节
- 工作原理是通过一个原先对象传给那个要创建的对象, 这个要发动创建的对象通过请求原对象拷贝他们自己来实施创建, 即对接.clone()
克隆羊问题
现有一直样, 名为Tom, 年龄为1, 颜色为白色, 编写程序创建Tom羊属性完全相同的10只羊
传统方式
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//传统的方法
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//....
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
System.out.println(sheep5);
//...
}
传统方式优缺点
- 代码容易理解, 简单易操作
- 在创建新的对象时, 总是需要重新获取原始对象的属性, 如果创建的对象比较复杂时, 效率低下
- 总是需要重新初始化对象, 而不是动态的获取对象运行时的属性, 不够灵活
原型模式方式
思路
java中object类是所有类的根类, object类提供了一个clone()方法, 该方法可以将一个java对象复制一份, 但是需要实现clone的java类必须实现cloneable接口, 该接口表示该类能够复制且具有复制的能力
代码
/**
* @Classname Sheep
* @Description
* @Date 2021/10/27 15:33
* @Author Yzx
*/
@Data
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
/**
* 是对象, 克隆是会如何处理, 默认是浅拷贝
*/
private Sheep friend;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep)super.clone();
} catch (Exception e){
e.printStackTrace();
}
return sheep;
}
}
/**
* @Classname Client
* @Description
* @Date 2021/10/27 15:50
* @Author Yzx
*/
public class Client {
public static void main(String[] args) {
System.out.println("原型模式完成对象的创建");
// TODO Auto-generated method stub
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.setFriend(new Sheep("jack", 2, "黑色"));
Sheep sheep2 = (Sheep)sheep.clone();
Sheep sheep3 = (Sheep)sheep.clone();
Sheep sheep4 = (Sheep)sheep.clone();
Sheep sheep5 = (Sheep)sheep.clone();
System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.getFriend().hashCode());
System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.getFriend().hashCode());
System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.getFriend().hashCode());
System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.getFriend().hashCode());
}
}
spring框架原型模式源码
在配置spring的bean时, 默认是单例的, 可以通过scope属性来设置单例还是原型
<bean id="sheep" class="com.yzx.demo.prototype" scope="prototype"/>
再起源码中可以发现当属性为原型时会调用createBean()方法创建一个原型实例
深拷贝与浅拷贝
浅拷贝
- 对于数据类型是基本数据类型的成员变量, 浅拷贝会直接进行值传递, 也就是将该属性复制一份给新的对象
- 对于数据类型是引用数据类型的成员变量, 如数组, 类对象等, 那么浅拷贝会进行引用传递, 也就是将该成员变量的引用在复制一份给新的对象, 实际上两个对象的该成员变量都指向同一个实例对象, 在这种情况下, 对其中一个对象的该成员变量的修改会影响到另一个对象的该成员变量
- object的clone()方法默认是浅拷贝
深拷贝
- 复制对象的所有基本数据类型的成员变量
- 为所有引用数据类型的成员变量申请储存控件, 并复制每个引用数据类型成员变量所引用对象, 直到该对象可达的所有对象, 也就是说, 对象进行深拷贝要对整个对象包括对象的引用类型都进行拷贝,修改该对象的任何属性都不会影响到另一个对象
- 通过重写clone()方法实现深拷贝
- 通过对象反序列化实现深拷贝
代码
/**
* @Classname DeepCloneableTarget
* @Description
* @Date 2021/10/28 9:31
* @Author Yzx
*/
@Data
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
/**
* 成员变量都为基本数据类型直接调用默认的clone方法实现浅拷贝
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* @Classname DeepProtoType
* @Description
* @Date 2021/10/29 14:36
* @Author Yzx
*/
@Data
public class DeepProtoType implements Serializable, Cloneable {
private String name;
private DeepCloneableTarget deepCloneableTarget;
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和 String 的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepProtoType;
}
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
//当前这个对象以对象流的方式输出
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType) ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/**
* @Classname Client
* @Description
* @Date 2021/10/29 14:40
* @Author Yzx
*/
public class Client {
public static void main(String[] args) {
DeepProtoType p = new DeepProtoType();
p.setName("宋江");
p.setDdeepCloneableTarget(new DeepCloneableTarget("大牛", "小牛"));
//方式 1 完成深拷贝
// DeepProtoType p2 = (DeepProtoType) p.clone();
//
// System.out.println("p.name=" + p.getName + "p.deepCloneableTarget=" + p.getDeepCloneableTarget.hashCode());
// System.out.println("p2.name=" + p.getName + "p2.deepCloneableTarget=" + p2.getDeepCloneableTarget.hashCode());
//方式 2 完成深拷贝
DeepProtoType p2 = (DeepProtoType) p.deepClone();
System.out.println("p.name=" + p.getName + "p.deepCloneableTarget=" + p.getDeepCloneableTarget.hashCode()); System.out.println("p2.name=" + p.getName + "p2.deepCloneableTarget=" + p2.getDeepCloneableTarget.hashCode());
}
}
注意事项
- 创建新的对象比较复杂时, 可以利用原型模式简化对象的创建过程, 同时也能提高效率
- 不用重复创建初始化对象, 而是动态的获取对象运行时的状态
- 如果原始对象发生变化, 增加或减少属性时, 其他克隆对象也会发生相应的变化, 无需修改代码
- 在实现克隆的时候可能需要比较复杂的代码
- 缺点: 需要为每一个类配备一个克隆方法, 这对权限的类不是很难, 但对已有的类进行改造时, 需要修改其他源代码, 违背了OCP原则