目錄
- 原型模式的基本介紹
- 原型模式(淺克隆)
- 原型模式(深克隆)
- 逐層克隆
- 序列化
序言:今天我們來聊一下原型模式,我個人認為原型模式的命名不太好理解,稱呼其為克隆模式會更妥當一點,原型模式的目的是通過復制一個現有的物件來生成一個新的物件,而不是通過實體化的方法,
原型模式的基本介紹
-
用已經創建的實體物件作為原型,通過復制物件來創建一個和原型相同的物件或相似的新物件
-
原型模式屬于創建型模式,主要通過
Cloneable介面去完成 物件的復制

在原型模式結構圖中,會有這么幾個角色
- 抽象原型角色(Prototype):是宣告克隆方法的介面,是所有原型類的公共父類
- 具體原型角色(Realizetype):它實作在抽象原型類中所宣告的克隆方法,在克隆方法中回傳一個克隆物件
- 訪問角色(PrototypeTest): 使用具體原型類中的 clone() 方法來復制新的物件
需要注意的點:
在 Java 中 能夠克隆的 Java類 務必得 實作 Cloneable 介面,表示這個 類 能夠被 “復制”,至于這個 復制的效果 則與我們的實作有關,通常 clone()方法滿足以下的條件:
- 對任何的物件x,都有:x.clone()!=x ,換言之,克隆物件與元物件不是一個物件
- 對任何的物件x,都有:x.clone().getClass==x.getClass(),換言之,克隆物件與元物件的型別一樣
- 對任何的物件x,如果 equals() 方法撰寫得當的話, 那么x.clone().equals(x)應該是成立的
在正式開始原型模式之前,我們先了解兩個概念 淺克隆和深克隆,淺克隆和深克隆的主要區別在于是否支持參考型別的成員變數的復制
原型模式(淺克隆)
在淺克隆中,如果原型物件的成員變數是值型別,將復制一份給克隆物件;如果原型物件的成員變數是參考型別,則將參考物件的地址 復制 一份給克隆物件,也就是說原型物件和克隆物件的成員變數指向 相同 的記憶體地址
下面我們以 花園和花 為案例
假設我們有一個花園,我們在花園里種植上好的牡丹花,有一天突然想照著牡丹園再建一個一模一樣的園子,只是在花園中改種玫瑰
/**
* 花園
*/
@Data
@AllArgsConstructor
public class Garden implements Cloneable {
//面積
private double area;
private Flower flower;
@Override
protected Garden clone() {
Garden garden = null;
try {
garden = (Garden) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return garden;
}
}
@Data
@AllArgsConstructor
public class Flower {
private String name;
private String color;
}
下面來測驗一下原型模式(淺克隆)
public class Client {
public static void main(String[] args) {
// 牡丹花
Flower peony = new Flower("牡丹", "blue");
// 牡丹園
Garden peonyGarden = new Garden(1000, peony);
System.out.println("初始的牡丹園:" + peonyGarden);
// 牡丹園的復制建造 但是花改成玫瑰 改名玫瑰園
Garden roseGarden = peonyGarden.clone();
//淺復制只會復制參考地址,并沒有重新復制一個物件
System.out.println(peonyGarden.getFlower() == roseGarden.getFlower());
// 改為種植玫瑰花
roseGarden.getFlower().setName("玫瑰");
roseGarden.getFlower().setColor("red");
System.out.println("玫瑰園:" + roseGarden);
System.out.println("牡丹園:" + peonyGarden);
}
}

我們發現,我們想在新開拓的花園中改種玫瑰后,會影響原花園的花卉的品種,這顯然不是我們想要的效果
從這里我們也看出來了,淺克隆 在克隆一個物件的 參考型別的成員變數時 只是復制其地址值,并沒有復制該物件
原型模式(深克隆)
在深克隆中,無論原型物件的成員變數是值型別還是參考型別,都將復制一份給克隆物件,深克隆將原型物件的所有參考物件也復制一份給克隆物件,簡單來說,在深克隆中,除了物件本身被復制外,物件所包含的所有成員變數也將復制,
在 Java 中想實作 深克隆,通常有 兩種方式
- 對于克隆物件的 參考型別,逐層克隆
- 使用序列化方式
逐層克隆
逐層克隆意味著,如果我們要拷貝一個物件,該物件中 若有多個參考型別的成員變數,它們都要實作克隆方法,若嵌套多層參考型別的成員變數,則逐層 實作 Cloneable 介面
**
* 花園
*/
@Data
@AllArgsConstructor
public class Garden implements Cloneable {
//面積
private double area;
private Flower flower;
@Override
protected Garden clone() {
Garden garden = null;
try {
garden = (Garden) super.clone();
garden.flower = garden.flower.clone();
} catch (Exception e) {
e.printStackTrace();
}
return garden;
}
}
@Data
@AllArgsConstructor
public class Flower implements Cloneable {
private String name;
private String color;
@Override
protected Flower clone() {
Flower flower = null;
try {
flower = (Flower) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return flower;
}
}
下面我們測驗一下,使用逐層實作 Cloneable 介面 而完成的深克隆
public class Client {
public static void main(String[] args) {
// 牡丹花
Flower peony = new Flower("牡丹", "blue");
// 牡丹園
Garden peonyGarden = new Garden(1000, peony);
// 牡丹園的復制建造 但是花改成玫瑰 改名玫瑰園
Garden roseGarden = peonyGarden.clone();
// 深克隆 面對參考型別的成員變數 也重新復制了一個物件
System.out.println(peonyGarden.getFlower() == roseGarden.getFlower());
// 改為種植玫瑰花
roseGarden.getFlower().setName("玫瑰");
roseGarden.getFlower().setColor("red");
System.out.println("玫瑰園:" + roseGarden);
System.out.println("牡丹園:" + peonyGarden);
}
}

序列化
深克隆模式,采取序列化這種方式可能更簡單一些,所以的參考型別 成員變數,都實作序列化介面,原型物件 自實作 deepClone 方法即可
/**
* 花園
*/
@Data
@AllArgsConstructor
public class Garden implements Serializable {
private static final long serialVersionUID = 2231850409918603998L;
//面積
private double area;
private Flower flower;
//深拷貝-方式2 使用序列化方式
public Garden deepClone(){
//創建流物件
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
Garden garden = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //當前這個物件以物件的方式輸出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
garden = (Garden) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return garden;
}
}
@Data
@AllArgsConstructor
public class Flower implements Serializable {
private static final long serialVersionUID = 126839939664064143L;
private String name;
private String color;
@Override
protected Flower clone() {
Flower flower = null;
try {
flower = (Flower) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return flower;
}
}
下面我們進行序列化深克隆的測驗
public class Client {
public static void main(String[] args) {
// 牡丹花
Flower peony = new Flower("牡丹", "blue");
// 牡丹園
Garden peonyGarden = new Garden(1000, peony);
// 牡丹園的復制建造 但是花改成玫瑰 改名玫瑰園
Garden roseGarden = peonyGarden.deepClone();
// 深克隆 面對參考型別的成員變數 也重新復制了一個物件
System.out.println(peonyGarden.getFlower() == roseGarden.getFlower());
// 改為種植玫瑰花
roseGarden.getFlower().setName("玫瑰");
roseGarden.getFlower().setColor("red");
System.out.println("玫瑰園:" + roseGarden);
System.out.println("牡丹園:" + peonyGarden);
}
}

從上述我們可以看出,深克隆不僅在堆記憶體上開辟了空間以存盤復制出的物件,甚至連物件中的參考型別的屬性所指向的物件也得以復制,重新開辟了堆空間存盤,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/4552.html
標籤:設計模式
上一篇:設計模式(7) 橋接模式
下一篇:二十三種設計模式修煉手冊
