原型模式不多單獨出現,通常都是和工廠方法模式一塊兒出現,它其實就是把對象生成的責任代理給本身,實現了自我複製,達成了對象拷貝的多態。今天簡單記錄一下原型模式。算法
使用原型實例指定建立對象的種類,而後經過拷貝這些原型來建立新的對象數組
使用者 須要創建一個原型,才能基於原型拷貝出新實例,還須要決策何時使用原型、何時使用新實例以及從原型到新實例之間的拷貝應該採用什麼樣的算法策略安全
查看以下代碼服務器
public interface PrototypeInterface extends Cloneable {
PrototypeInterface clone() throws CloneNotSupportedException;
}
public class ProtypeA implements PrototypeInterface {
@Override
public ProtypeA clone() throws CloneNotSupportedException {
System.out.println("Cloning new object: A");
return (ProtypeA) super.clone();
}
}
public class ProtypeB implements PrototypeInterface {
@Override
public ProtypeB clone() throws CloneNotSupportedException {
System.out.println("Cloning new object: B");
return (ProtypeB) super.clone();
}
}
//ProtypeA以本身爲原型經過拷貝建立一個新的對象newInstanceA
public static void main(String[] args) throws CloneNotSupportedException {
ProtypeA source = new ProtypeA();
System.out.println(source);
ProtypeA newInstanceA = source.clone();
System.out.println(newInstanceA);
}
複製代碼
原型模式封裝了以下變化:markdown
原始對象的構造方式,clone 方法的原理是從內存種以二進制流的方式進行拷貝,從新分配內存塊,因此構造函數不會執行網絡
對象的屬性與其餘對象間的依賴關係ide
對象運行時狀態的獲取方式函數
對象拷貝的具體實現策略 clone 方法的實現性能
因此說,原型模式從創建原型到拷貝原型生成新實例,都是對用戶透明的,一旦中間任何一個小細節出現問題,你可能獲取的就是一個錯誤的對象。優化
public class Test implements Cloneable{
private ArrayList<String> arrayList = new ArrayList<>();
@Override
public Test clone(){
Test test = null;
try{
test = (Test)super.clone();
}catch(CloneNotSupportedException e){
}
return test;
}
public void setValue(String value){
this.arrayList.add(value);
}
public ArrayList<String> getValue(){
return this.arrayList;
}
}
public class TestDemo{
public static void main(String[] args){
Test test = new Test();
test.setValue("我");
Test cloneTest = test.clone();
cloneTest.setValue("你");
System.out.println(test.getValue());
}
}輸出:
[我,你]
複製代碼
按咱們的意願,應該只輸出 「我」,克隆對象不該該改變原型對象的值,這裏卻改變了,爲何呢?
Object 類提供的方法clone只是拷貝對象,其內部的數組、引用對象等都不拷貝,仍是指向原型對象的內部元素地址,這種拷貝就叫淺拷貝。很是不安全。可是內部的基本類型,例如int、long、char 都會被拷貝,對於String類型,能夠理解爲是基本類型,它沒有clone方法。
咱們對上面的clone
代碼修改一下
@Override
public Test clone(){
Test test = null;
try{
test = (Test)super.clone();
test.arrayList = (ArrayList<String>)this.arrayList.clone();
}catch(CloneNotSupportedException e){
}
return test;
}
複製代碼
其實就是單獨對私有變量拷貝就能夠了,這種方式的拷貝,兩個對象間就沒有任何瓜葛了,這就是深拷貝。
固然,深拷貝還能夠經過本身寫二進制流的方式來操做,相似下面的操做
public Test deepClone() throws IOException,ClassNotFoundException{
//將對象放入流
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//將象從流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Test)ois.readObject();
}
複製代碼
資源優化場景。也就是當進行對象初始化須要使用不少外部資源時,好比,IO 資源、數據文件、CPU、網絡和內存等。
複雜的依賴場景。 好比,F 對象的建立依賴 A,A 又依賴 B,B 又依賴 C……因而建立過程是一連串對象的 get 和 set。
性能和安全要求的場景。 好比,同一個用戶在一個會話週期裏,可能會反覆登陸平臺或使用某些受限的功能,每一次訪問請求都會訪問受權服務器進行受權,但若是每次都經過 new 產生一個對象會很是煩瑣,這時則可使用原型模式。
同一個對象可能被多個修改者使用的場景。 好比,一個商品對象須要提供給物流、會員、訂單等多個服務訪問,並且各個調用者可能都須要修改其值時,就能夠考慮使用原型模式。
須要保存原始對象狀態的場景。 好比,記錄歷史操做的場景中,就能夠經過原型模式快速保存記錄。
結合工廠模式來使用。 在實際項目中,原型模式除了單獨基於對象使用外,還能夠結合工廠方法模式一塊兒使用,經過定義統一的複製接口,好比 clone、copy。使用一個工廠來統一進行拷貝和新對象建立, 而後由工廠方法提供給調用者使用。
逃避了構造函數的約束
減小每次建立對象的資源消耗
下降對象建立的時間消耗
快速複製對象運行時狀態,複製大對象時,性能更優
能保存原始對象的副本
原型須要一個被初始化過的正確對象
複製大對象時,可能出現內存溢出的 OOM 錯誤
動態擴展對象功能時可能會掩蓋新的風險。好比,埋點服務中咱們一般會拷貝一份對象在某個時間節點的數據,並添加一些追蹤數據後再推送給埋點服務,這樣就可能增長過多的內存消耗,影響原有功能執行的性能,有時還可能引發 OOM,致使系統宕機