設計模式 原型模式介紹 原型模式的深拷貝問題

2022-01-21 19:42:56 字數 4625 閱讀 8581

轉殖羊問題:有乙個羊,是乙個類,有對應的屬性,要求建立完全一樣的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.原型模式可以簡單理解為拷貝原型物件得到新的物件。想象乙個配鑰匙的小店,給店主乙個原有...

設計模式 原型模式

魔術師手拿一張百元大鈔,瞬間又變出兩張。也像配鑰匙一樣,拿一把鑰匙,老師傅就能做出另乙個一模一樣的。像這種複製我們並不陌生,類似於我們設計中的原型模式 本文將從以下幾點 原型模式 概述 結構圖 淺複製深複製 總結 用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。允許乙個物件再建立另外...