原型模式
原型模式通用写法是指通过拷贝原型实例来创建新的对象,具体来说是通过系统中已经存在的实例为原型,基于内存二进制流进行拷贝来创建新的对象;不调用构造函数。属于创建型模式。
1. 因为是基于二进制流创建对象,相比基于构造函数创建对象,性能大大提高
2. 可以通过来保存对象的中间状态,可用于数据的回滚操作。
1. 需要类实现克隆方法。
2. 克隆方法在类内部,对已有对象进行改造时违法开闭原则。
3. 深度克隆需要编写较复杂的代码,如果当前对象存在多重嵌套,需要引用的类都支持深度克隆,实现比较麻烦,[注:可通过 JSON.parseObject(json,new TypeReference<Object>(){});
来规避。]
1. 类初始化消耗资源较多,如创建数据连接。
2. 通过构造函数创建对象过程比较繁琐,如权限访问。
3. 构造函数比较复杂。
4. 循环创建大量对象。
JDK源码中的ArrayList.clone()方法通过原型模式实现,通过实现Cloneable接口的clone()方法来实现浅克隆。
public class ArrayListextends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
}
浅clone
只是完整复制了值类型的数据,没有给引用对象赋值,使得引用对象仍指向原来对象的地址。
1. 规避了通过构造函数创建对象。
1. 如果实例中存在引用对象,当拷贝对象的引用成员变量被修改时,原的对象该引用也被修改了
public class ConcretePrototype {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
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) {
ConcretePrototype prototype = (ConcretePrototype)BeanUtils.copy(new ConcretePrototype());
System.out.println(prototype);
}
}
深clone
通过JSON或者序列化的方式(如:二进制流方式)使得引用对象得以赋值,[注意深度clone破坏了单例模式,二者是不能共存的。]
抽象工厂非常完美清晰地描述了产品族和产品等级结构之间的关系--即抽象工厂可以理解为工厂方法的集合。
@Getter
@Setter
public class Person implements Serializable,Cloneable {
/**
* 年龄
*/
private int age;
/**
* 姓名
*/
private String name;
/**
* 爱好
*/
private List hobbies;
}
@Setter
@Getter
public class Course implements Serializable {
private String courseName;
}
@Getter
@Setter
public class Student extends Person {
private Course course;
private Timestamp birthday;
/**
* 深克隆
*
* @return
*/
public Student deepClone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(bos);
outputStream.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream inputStream = new ObjectInputStream(bis);
Student result = (Student)inputStream.readObject();
return result;
}catch (Exception e){
System.out.println("Failed to deep clone, reason is "+ e.getMessage());
return null;
}
}
}
public class DeepCloneTest {
public static void main(String[] args) {
Student student = new Student();
student.setName("小明");
List<String> hobbies = new ArrayList<String>(1);
hobbies.add("game");
student.setHobbies(hobbies);
student.setAge(10);
student.setCourse(new Course());
student.setBirthday(new Timestamp(System.currentTimeMillis()));
Student deepClone = student.deepClone();
System.err.println("深克隆:" + student.getCourse().equals(deepClone.getCourse()));
}
浅clone和深clone的区别
1. 实现Cloneable接口的都是浅clone
2. 通过序列化或转Json可实现深clone。
原型模式和单例模式的区别
原型模式的实例是不同的,单例模式创建出来的对象实例是相同的。
总结
原型模式的核心是通过原型实例拷贝得到新的实例对象,适用于对象创建较为复杂的场景如构造函数较复杂(成员变量数量较大的情况),深度克隆相对浅克隆解决了引用对象赋值问题,但是增加了代码编写难度和内存占用(主要是因为破坏了单例模式)
网友评论