設計模式學習(五):原型模式
設計模式學習(五):原型模式
作者:Grey
原文地址:
原型模式
原型模式是創建型模式。
如果對象的創建成本比較大,而同一個類的不同對象之間差別不大(大部分欄位的值都相同),在這種情況下,我們可以利用對已有對象(原型)進行複製(或者叫拷貝)的方式來創建新對象,以達到節省創建時間的目的。這種基於原型來創建對象的方式就叫作原型設計模式。
實際上,創建對象包含的申請記憶體、給成員變數賦值這一過程,本身並不會花費太多時間,或者說對於大部分業務系統來說,這點時間完全是可以忽略的。應用一個複雜的模式,只得到一點點的性能提升,這就是所謂的過度設計,得不償失。但是,如果對象中的數據需要經過複雜的計算才能得到(比如排序、計算哈希值),或者需要從 RPC 、網路、資料庫、文件系統等非常慢速的 IO 中讀取,每次讀取一次的代價都很高,在這種情況下,我們就可以利用原型模式,從其他已有對象中直接拷貝得到,而不用每次在創建新對象的時候,都重複執行這些耗時的操作。
原型模式用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象,典型的應用是對象的克隆方法,示例程式碼如下
public class Person implements Cloneable {
String name = "lisa";
int age = 1;
Location loc = new Location("xy", 10);
@Override
protected Object clone() throws CloneNotSupportedException {
Person p = (Person) super.clone();
p.loc = (Location) loc.clone();
return p;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + ", loc=" + loc + '}';
}
}
public class Location implements Cloneable {
private String street;
private int roomNo;
public Location(String street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Location{" + "street='" + street + '\'' + ", roomNo=" + roomNo + '}';
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Person p = new Person();
System.out.println(p);
Person p2 = (Person) p.clone();
System.out.println(p2);
}
}
本示例的 UML 圖如下:
註:Java 自帶的 clone()
方法進行的就是淺克隆。而如果我們想進行深克隆,可以直接在類中調用父類的克隆方法,即: super.clone()
後,手動給克隆對象的相關屬性分配另一塊記憶體,不過如果當原型對象維護很多引用屬性的時候,手動分配會比較煩瑣。因此,在 Java 中,如果想完成原型對象的深克隆,則通常使用序列化的方式。
例如:克隆一個巨大的 HashMap,如果構建該 HashMap 的代價很大,我們可以通過
方式一:先調用 HashMap 默認的克隆方法,然後遞歸拷貝 HashMap 裡面的內容,直到類型是基礎類型為止;
方式二:使用序列化方式克隆。
關於序列化克隆的示例程式碼如下
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class DeepCloneDemo {
public static void main(String[] args) {
List<String> hobbies = new ArrayList<>();
hobbies.add("唱歌");
hobbies.add("跳舞");
Prototype p = new Prototype();
p.setAge(18);
p.setName("zhangsan");
p.setHobbits(hobbies);
Prototype clone = p.clone();
System.out.println(clone.getAge());
System.out.println(clone.getName());
System.out.println(clone.getHobbits());
}
}
// 待克隆對象
class Prototype implements Cloneable, Serializable {
private int age;
private String name;
private List<String> hobbits;
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;
}
public List<String> getHobbits() {
return hobbits;
}
public void setHobbits(List<String> hobbits) {
this.hobbits = hobbits;
}
@Override
protected Prototype clone() {
return deepClone();
}
public Prototype deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Prototype) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "Prototype{" + "age=" + age + ", name='" + name + '\'' + ", hobbits=" + hobbits + '}';
}
}
如果只是增量拷貝,可以通過淺拷貝拿到一個新的 HashMap ,然後拿到增量的數據單獨進行深拷貝即可。
更多地,Spring 中創建對象的方式默認採用單例模式,可以通過設置 @Scope("prototype")
註解將其改為原型模式。