## **1. 定義**
>**原型模式(Prototype Pattern)**:使用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
簡單地說:相信大多數的人都看過《西游記》,對孫悟空拔毛變出小猴子的故事情節應該都很熟悉。孫悟空可以用猴毛根據自己的形象復制出很多跟自己一模一樣的小猴兵出來,其實在設計模式中也有一個類似的模式,我們可以通過一個原型對象來克隆出多個一模一樣的對象,這個模式就是原型模式。
## **2. 場景**
>M公司一直在使用自行開發的一個OA系統進行日常工作辦理,但在使用過程中,越來越多的人對工作周報的創建和編寫模塊產生了抱怨。追其原因,M公司的OA管理員發現,由于某些崗位每周工作存在重復性,工作周報內容都大同小異,如下圖所示:

>這些周報只有一些小地方存在差異,但是現行系統每周默認創建的周報都是空白報表,因此用戶只能通過重新輸入或不斷地復制與粘貼來填寫重復的周報內容,極大地降低了工作效率,浪費寶貴的時間。如何快速創建相同或者相似的工作周報,成為了M公司軟件開發人員的一個新問題。
M公司開發人員經過分析,決定按照以下思路對工作周報模塊進行重新設計:
(1)除了允許用戶創建新周報外,還允許用戶將創建好的周報保存為模板(也就是原型)。
(2)用戶在再次創建周報時,可以創建全新的周報,還可以選擇合適的模板復制生成一個相同的周報,然后對新生成的周報根據實際情況進行修改,產生新的周報。
`需要注意的是,通過克隆方法所創建的對象時全新的對象。`
>原型模式中的角色包括:
● Prototype(抽象原型類):它是聲明克隆方法的接口,是所有具體原型類的公共父類,可以是抽象類也可以是接口,甚至還可以是具體實現類。?
● ConcretePrototype(具體原型類):它實現在抽象原型類中聲明的克隆方法,在克隆方法中返回自己的一個克隆對象?
● Client(客戶類):**讓一個原型對象克隆自身從而創建一個新的對象,在客戶類中只需要**直接實例化或通過工廠方法等方式創建一個原型對象**,再通過**調用該對象的克隆方法即可得到多個相同的對象**。由于客戶類針對抽象原型類Prototype編程,因此用戶可以根據需要選擇具體原型類,系統具有較好的可擴展性,增加或更換具體原型類都很方便。
`結構圖圖下:`

## **3. 代碼實現**
以克隆羊為例:傳統的解決方式
```
public class Sheep {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {return name; }
public void setName(String name) {this.name = name; }
public int getAge() {return age; }
public void setAge(int age) {this.age = age; }
public String getColor() {return color; }
public void setColor(String color) { this.color = color; }
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
```
```
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "white");
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
}
}
```
>1)在創建對象時,總是需要重新獲取原始對象的屬性,如果創建的對象比較復雜時,效率較低。
2)總是需要重新初始化對象,而不是動態地獲取對象的運行狀態
**淺拷貝思路:** Java的Object類提供了一個clone()方法,該方法可以將一個java對象復制一份,但是需要事先clone的java類必須要事先一個接口Cloneable。
**基本介紹:**
1)原型模式是指:用原型實例指定創建對象的種類,并且通過拷貝原型 創建對象。
2)是一種創建型設計模式,允許一個對象再創建一個可定制的對象,無需知道如何創建的細節。
對于數據類型是基本數據類型的成員變量,淺拷貝直接進行值傳遞,引用型數據類型,會將成員變量的引用值復制一份給新的對象。

```
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private String address="蒙古羊";
public Sheep friend;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", address='" + address + '\'' +
'}';
}
//克隆該實例,使用默認的clone方法完成
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
}
```
```
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "white");
sheep.friend=new Sheep("jack",2,"black");//加入引用型對象屬性
Sheep clone = (Sheep) sheep.clone();
Sheep clone2 = (Sheep) sheep.clone();
Sheep clone3 = (Sheep) sheep.clone();
Sheep clone4 = (Sheep) sheep.clone();
Sheep clone5 = (Sheep) sheep.clone();
System.out.println(clone2+"clone2"+clone2.friend.hashCode());
System.out.println(clone3+"clone3"+clone3.friend.hashCode());//hashcode值一樣
System.out.println(clone==clone2);//false
}
}
```
>**深拷貝:**
1)復制對象的基本數據類型的成員變量值
2)為所有引用數據類型的成員變量申請存儲空間,并復制每個引用數據類型成員變量鎖引用的對象,直到該對象所達的所有對象。即 對象進行深拷貝就是對整個對象(包括對象的引用類型)進行拷貝。
`方式1:重寫clone方法實現深拷貝`
`方式2:通過對象序列化實現深拷貝`
```
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
```
```
public class DeepProtoType implements Serializable, Cloneable{
public String name;
public DeepCloneableTarget deepCloneableTarget;
public DeepProtoType() {
super();
}
//方式一:深拷貝 使用clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
deep = super.clone();//對象的深拷貝
//強轉成哪種類的對象,就是哪種類要實現Serializable, Cloneable接口進行深拷貝
DeepProtoType deepProtoType = (DeepProtoType)deep;//類型強轉
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();//對象的引用類型的深拷貝
return deepProtoType;
}
// 方式二: 通過對象的序列化 實現深拷貝 (推薦)
public Object deepClone() {
//創建流對象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);//當前這個對象以對象流的方式輸出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
```
```
public class Client {
public static void main(String[] args) throws Exception {
DeepProtoType p = new DeepProtoType();
p.name = "宋江";
p.deepCloneableTarget = new DeepCloneableTarget("大牛", "大牛的類");
//方式1:深拷貝
DeepProtoType p2 = (DeepProtoType) p.clone();
System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
System.out.println("p2.name=" + p2.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
// DeepProtoType p2 = (DeepProtoType) p.deepClone();
//
// System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
// System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
}
}
```
- 前言
- 第一章 設計七大原則
- 第1節 開閉原則
- 第2節 依賴倒置原則
- 第3節 單一職責原則
- 第4節 接口隔離原則
- 第5節 迪米特法則
- 第6節 里氏替換原則
- 第7節 合成復用原則
- 第二章 簡單工廠模式
- 第1節 使用場景
- 第2節 示例代碼
- 第三章 創建者模式
- 第1節 工廠方法模式
- 第2節 抽象工廠模式
- 第3節 建造者模式
- 第4節 原型模式
- 第5節 單例模式
- 第四章 結構型模式
- 第1節 適配器模式
- 第2節 橋接模式
- 第3節 組合模式
- 第4節 裝飾者模式
- 第5節 外觀模式
- 第6節 享元模式
- 第7節 代理模式
- 第五章 行為模式
- 第1節 責任鏈模式
- 第2節 命令模式
- 第3節 迭代器模式
- 第4節 中介者模式
- 第5節 備忘錄模式
- 第6節 觀察者模式
- 第7節 狀態模式
- 第8節 策略模式
- 第9節 模板方法模式
- 第10節 訪問者模式
- 第11節 解釋器模式