轉殖羊問題:有乙個羊,是乙個類,有對應的屬性,要求建立完全一樣的10隻羊出來。
那麼實現起來很簡單,我們先寫出羊的類:
public class sheep
然後,建立10只一樣的羊,就在客戶端寫乙個**建立:
//原始羊
sheep sheep = new sheep("tom",1,"白色");
//轉殖羊
sheep sheep1 = new sheep(sheep.getname(),sheep.getage(),sheep.getcolor());
sheep1 是轉殖的第一隻羊,接著就可以複製十遍這個**,然後命名不同的羊,以原始sheep為模板進行轉殖。
這種方法的弊端:
建立新物件,總是需要重新獲取原始物件的屬性值,效率低;
總是需要重新初始化物件,而不是動態獲取物件執行時的狀態,不靈活。(什麼意思呢,比如原始的 sheep 有一項要修改,那麼剩下的以它為範本的,必然要重新初始化)
原型模式指的是,用原型例項指定建立物件的種類,並通過拷貝這些原型,建立新的物件;
原型模式是一種建立型設計模式,允許乙個物件再建立另乙個可以定製的物件,無需知道如何建立的細節;
工作原理是:發動建立的這個物件,請求原型物件,讓原型物件來自己實施建立,就是原型物件.clone()。
如下類圖所示:
其中,prototype 是乙個原型介面,在這裡面把轉殖自己的方法宣告出來;
concreteprotype 可以是一系列的原型類,實現具體操作。
對於轉殖羊問題,我們來利用原型設計模式進行改進:
讓sheep類,實現 cloneable 介面:
public class sheep implements cloneable catch (clonenotsupportedexception e)
return sheep;}}
現在的 sheep 類就是乙個具體的原型實現類了,我們想要轉殖的時候,客戶端呼叫可以這樣:
sheep sheep1 = (sheep) sheep.clone();
sheep sheep2 = (sheep) sheep.clone();
//。。。。。類似
這種做法就是原型設計模式。
(spring框架裡,通過bean標籤配置類的scope為prototype,就是用的原型模式)使用上面所說的原型模式,按理說是複製出了一模一樣的物件。
但我們做乙個嘗試,如果sheep 類裡的成員變數有乙個是物件,而不是基礎型別呢?
private sheep friend;
然後我們建立、再轉殖:
sheep sheep = new sheep("tom",1,"白色");//原始羊
sheep.setfriend(new sheep("jack",2,"黑色"));
sheep sheep1 = (sheep) sheep.clone();
sheep sheep2 = (sheep) sheep.clone();
sheep sheep3 = (sheep) sheep.clone();
重寫一下 sheep 類的 tostring 方法,輸出資訊和對應的屬性的 hashcode 後會發現:
sheep
sheep
sheep
friend 的 hashcode 值都一樣,也就是轉殖的類的 friend 屬性其實沒有被複製,而是指向了同乙個物件。這就叫淺拷貝(shallow copy):
對於資料型別是基本資料型別的成員變數,淺拷貝會直接進行值傳遞,也就是複製乙份給新物件;
對於資料型別是引用資料型別的成員變數,淺拷貝會進行引用傳遞,也就是只是將位址指標複製乙份給新物件,實際上覆制前和複製後的內容都指向同乙個例項。這種情況,顯然在乙個物件裡修改成員變數,會影響到另乙個物件的成員變數值(因為修改的都是同乙個)
預設的 clone() 方法就是淺拷貝。
在原始碼裡也說明了,這個方法是shallow copy 而不是 deep copy。
在實際開發中,往往是希望轉殖的過程中,如果類的成員是引用型別,也能完全轉殖乙份,也就是所謂的深拷貝。
深拷貝(deep copy):
複製物件的所有基本資料型別成員變數值;
為所有引用資料型別的成員變數申請儲存空間,並且也複製每個 引用資料型別的成員變數 引用的 所有物件,一直到該物件可達的所有物件;
深拷貝的實現方式,需要通過重寫 clone 方法,或者通過物件的序列化。
下面來實現一下。
/*
被拷貝的類引用的類,此類的clone用預設的clone即可
*/public class clonetarget implements cloneable
@override
protected object clone() throws clonenotsupportedexception
}
/*
原型類,其中有成員是引用型別,因此clone方法要重寫達到深拷貝
*/public class prototype implements cloneable
@override
protected object clone() throws clonenotsupportedexception
}
這樣的話,新建乙個原型prototype的物件後,對他進行轉殖,得到的裡面的 clonetarget 成員也是深拷貝的兩個不一樣的物件了。
但是這種方法本質上是相當於套娃,因為都要單獨處理重寫 clone 方法,所以有些麻煩。
在 prototype 裡直接使用序列化+反序列化,達到對這個物件整體的乙個複製。
另外注意,序列化和反序列化,必須實現 serializable 介面,所以 implements 後面不止要有 cloneable,還有serializable。
//利用序列化實現深拷貝
public object deepclone() catch (ioexception | classnotfoundexception e) finally catch (ioexception e)
}return null;
}
然後我們想要轉殖的時候,直接呼叫這個 deepclone 方法就可以達到目的。
忽視掉裡面的 try - catch 之類的**,其實核心部分就是用到序列化和反序列化的總共 4 個物件。這種方法是推薦的,因為實現起來更加容易。
序列化反序列化達到深拷貝目的的原理:
在這裡,我們採用的outputstream是bytearrayoutputstream——位元組陣列輸出流,通過建立的objectoutputstream的writeobject方法,把物件寫進了這個位元組陣列輸出流。
在這裡,把位元組陣列重新構造成乙個bytearrayinputstream——位元組陣列輸入流,通過objectinputstream的readobject方法,把輸入流重新構造成乙個物件。
結合上面的**再看看:
bos = new bytearrayoutputstream();
oos = new objectoutputstream(bos);//寫入指定的outputstream
oos.writeobject(this);//把物件寫入到輸出流中,整個物件,this
bis = new bytearrayinputstream(bos.tobytearray());
ois = new objectinputstream(bis);//讀取指定的inputstream
prototype copy = (prototype) ois.readobject();//從輸入流中讀取乙個物件
return copy;
原型模式:當需要建立乙個新的物件的內容比較複雜的時候,可以利用原型模式來簡化建立的過程,同時能夠提高效率。
因為這樣不用重新初始化物件,而是動態地獲得物件執行時的狀態,如果原始的物件內部發生變化,其他轉殖物件也會發生相應變化,無需一 一修改。
實現深拷貝的方法要注意。
缺點:
每乙個類都需要乙個轉殖方法,對於全新的類來說不是問題,但是如果是用已有的類進行改造,那麼可能會因為要修改源**而違背 ocp 原則。
原型模式介紹
原型模式是一種建立型設計模式,允許乙個物件再建立另外乙個可定製的物件,無需知道如何建立的細節。原型模式 prototype模式 是指 用原型例項指定建立物件的種類,並且通過拷貝這些原型,建立新的物件。原型模式工作原理是 通過將乙個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件...
設計模式 原型模式
1.首先分析原型模式的由來 一般來說,建立乙個物件可以由以下方法 知道物件的具體型別,直接用new生成。不知道型號,知道相應的需求,可以使用工廠方法模式。根據乙個已有的物件來複製為乙個新的物件,可以使用原型模式。2.原型模式可以簡單理解為拷貝原型物件得到新的物件。想象乙個配鑰匙的小店,給店主乙個原有...
設計模式 原型模式
魔術師手拿一張百元大鈔,瞬間又變出兩張。也像配鑰匙一樣,拿一把鑰匙,老師傅就能做出另乙個一模一樣的。像這種複製我們並不陌生,類似於我們設計中的原型模式 本文將從以下幾點 原型模式 概述 結構圖 淺複製深複製 總結 用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。允許乙個物件再建立另外...