定義
通過給出一個原型對象來指明所要建立的對象的類型,然後用複制這個原型對象的辦法建立出更多同類型的對象。這是原型模式的用意。
原型模式的結果有兩種,一種是淺複制,一種是深複制。
淺複制
用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對象并釋放其所持有的資源,都是用戶端代碼的職責。