定义
通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。这是原型模式的用意。
原型模式的结果有两种,一种是浅复制,一种是深复制。
浅复制
用Java方式实现浅复制,被复制对象必须实现Cloneable接口。
package com.faith.net.prototype.simple;
import java.util.List;
/**
* 简历类
*/
public class Resume implements Cloneable {
private String name;
private Integer age;
private List<String> blogs;
public Resume() { }
public Resume(String name, Integer age) {
this.name = name;
this.age = age;
}
···省略getter、setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试:
package com.faith.net.prototype.simple;
import java.util.ArrayList;
import java.util.List;
/**
* 测试类
*/
public class CloneTest {
public static void main(String[] args) throws Exception {
Resume resume = new Resume("faith", 28);
List<String> blogs = new ArrayList<String>(2);
blogs.add("yunqi");
resume.setBlogs(blogs);
Resume clone = (Resume) resume.clone();
System.out.println(clone.getName() + ": " + clone.getAge()); // faith: 28
System.out.println(clone.getBlogs().get(0)); // yunqi
blogs.set(0, "csdn");
System.out.println(clone.getBlogs().get(0)); // csdn
}
}
修改resume对象的blogs属性,却使得clone对象的blogs属性也同时改变了,能够看出这里是浅复制,对于引用对象类型的成员,只是复制了引用地址,而没有重新生成新的拷贝对象。
深复制
实现深复制就要将深复制的逻辑写到clone方法中,如下:
package com.faith.net.prototype.simple;
import java.io.*;
import java.util.List;
/**
* 简历类
*/
public class Resume implements Cloneable, Serializable {
private String name;
private Integer age;
private List<String> blogs;
public Resume() { }
public Resume(String name, Integer age) {
this.name = name;
this.age = age;
}
···省略getter、setter
@Override
protected Object clone() {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Resume clone = (Resume)ois.readObject();
clone.age = this.age + 1;
return clone;
}catch (Exception e){
throw new RuntimeException();
}
}
}
测试类:
/**
* 测试类
*/
public class CloneTest {
public static void main(String[] args) throws Exception {
Resume resume = new Resume("faith", 28);
List<String> blogs = new ArrayList<String>(2);
blogs.add("yunqi");
resume.setBlogs(blogs);
Resume clone = (Resume) resume.clone();
System.out.println(clone.getName() + ": " + clone.getAge()); // faith: 29
System.out.println(clone.getBlogs().get(0)); // yunqi
blogs.set(0, "csdn");
System.out.println(clone.getBlogs().get(0)); // yunqi
}
}
值得注意的是,这里的clone方法中的逻辑可以通过多种方式实现,只要能重新创建引用类型对象即可,反序列化方式能够重新创建List类型对象,所以这里能够应用。
还可以使用反射方式,Spring中大量使用了反射实现深复制;或者直接将原对象中的blogs取出,循环遍历元素并添加到新的List对象中。
Spring中的原型模式
Spring中的原型模式大都是使用反射实现的。这里讲一下应用场景:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" scope="singleton"/>
这里的scope是用来配置spring bean的作用域,默认为singleton,Spring IOC容器中只会存在一个共享的bean实例,所有对bean的请求只会返回同一实例。
当scope值为prototype,即为原型模式的应用,每一次请求(将其注入到另一个bean,或以程序方式调用容器的getBean()方法)都会产生一个新的bean实例,相当于new操作。
容器在装配完prototype实例后交给客户端,随后就对该prototype实例不闻不问。而清除prototype对象并释放其所持有的资源,都是客户端代码的职责。