# 對象的克隆——原型模式(一)
張紀中版《西游記》以出乎意料的造型和雷人的臺詞遭到廣大觀眾朋友的熱議,我們在此對該話題不作過多討論。但無論是哪個版本的《西游記》,孫悟空都是其中的一號雄性主角,關于他(或它)拔毛變小猴的故事幾乎人人皆知,孫悟空可以用猴毛根據自己的形象,復制(又稱“克隆”或“拷貝”)出很多跟自己長得一模一樣的“身外身”來。在設計模式中也存在一個類似的模式,可以通過一個原型對象克隆出多個一模一樣的對象,該模式稱之為原型模式。
7.1 大同小異的工作周報
Sunny軟件公司一直使用自行開發的一套OA (Office Automatic,辦公自動化)系統進行日常工作辦理,但在使用過程中,越來越多的人對工作周報的創建和編寫模塊產生了抱怨。追其原因,Sunny軟件公司的OA管理員發現,由于某些崗位每周工作存在重復性,工作周報內容都大同小異,如圖7-1工作周報示意圖。這些周報只有一些小地方存在差異,但是現行系統每周默認創建的周報都是空白報表,用戶只能通過重新輸入或不斷復制粘貼來填寫重復的周報內容,極大降低了工作效率,浪費寶貴的時間。如何快速創建相同或者相似的工作周報,成為Sunny公司OA開發人員面臨的一個新問題。

圖7-1 工作周報示意圖
Sunny公司的開發人員通過對問題進行仔細分析,決定按照如下思路對工作周報模塊進行重新設計和實現:
(1)除了允許用戶創建新周報外,還允許用戶將創建好的周報保存為模板;
(2)用戶在再次創建周報時,可以創建全新的周報,還可以選擇合適的模板復制生成一份相同的周報,然后對新生成的周報根據實際情況進行修改,產生新的周報。
只要按照如上兩個步驟進行處理,工作周報的創建效率將得以大大提高。這個過程讓我們想到平時經常進行的兩個電腦基本操作:復制和粘貼,快捷鍵通常為Ctrl + C和Ctrl + V,通過對已有對象的復制和粘貼,我們可以創建大量的相同對象。如何在一個面向對象系統中實現對象的復制和粘貼呢?不用著急,本章我們介紹的原型模式正為解決此類問題而誕生。
7.2 原型模式概述
在使用原型模式時,我們需要首先創建一個原型對象,再通過復制這個原型對象來創建更多同類型的對象。試想,如果連孫悟空的模樣都不知道,怎么拔毛變小猴子呢?原型模式的定義如下:
原型模式(Prototype Pattern):使用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。原型模式是一種對象創建型模式。
原型模式的工作原理很簡單:將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝自己來實現創建過程。由于在軟件系統中我們經常會遇到需要創建多個相同或者相似對象的情況,因此原型模式在真實開發中的使用頻率還是非常高的。原型模式是一種“另類”的創建型模式,創建克隆對象的工廠就是原型類自身,工廠方法由克隆方法來實現。
需要注意的是通過克隆方法所創建的對象是全新的對象,它們在內存中擁有新的地址,通常對克隆所產生的對象進行修改對原型對象不會造成任何影響,每一個克隆對象都是相互獨立的。通過不同的方式修改可以得到一系列相似但不完全相同的對象。
原型模式的結構如圖7-2所示:
圖7-2 原型模式結構圖
在原型模式結構圖中包含如下幾個角色:
●Prototype(抽象原型類):它是聲明克隆方法的接口,是所有具體原型類的公共父類,可以是抽象類也可以是接口,甚至還可以是具體實現類。
● ConcretePrototype(具體原型類):它實現在抽象原型類中聲明的克隆方法,在克隆方法中返回自己的一個克隆對象。
● Client(客戶類):讓一個原型對象克隆自身從而創建一個新的對象,在客戶類中只需要直接實例化或通過工廠方法等方式創建一個原型對象,再通過調用該對象的克隆方法即可得到多個相同的對象。由于客戶類針對抽象原型類Prototype編程,因此用戶可以根據需要選擇具體原型類,系統具有較好的可擴展性,增加或更換具體原型類都很方便。
原型模式的核心在于如何實現克隆方法,下面將介紹兩種在Java語言中常用的克隆實現方法:
1.通用實現方法
通用的克隆實現方法是在具體原型類的克隆方法中實例化一個與自身類型相同的對象并將其返回,并將相關的參數傳入新創建的對象中,保證它們的成員屬性相同。示意代碼如下所示:
```
class ConcretePrototype implements Prototype
{
private String attr; //成員屬性
public void setAttr(String attr)
{
this.attr = attr;
}
public String getAttr()
{
return this.attr;
}
public Prototype clone() //克隆方法
{
Prototype prototype = new ConcretePrototype(); //創建新對象
prototype.setAttr(this.attr);
return prototype;
}
}
```
思考
能否將上述代碼中的clone()方法寫成:public Prototype clone() { return this; }?給出你的理由。
在客戶類中我們只需要創建一個ConcretePrototype對象作為原型對象,然后調用其clone()方法即可得到對應的克隆對象,如下代碼所示:
```
Prototype obj1 = new ConcretePrototype();
obj1.setAttr("Sunny");
Prototype obj2 = obj1.clone();
```
這種方法可作為原型模式的通用實現,它與編程語言特性無關,任何面向對象語言都可以使用這種形式來實現對原型的克隆。
2. Java語言提供的clone()方法
學過Java語言的人都知道,所有的Java類都繼承自java.lang.Object。事實上,Object類提供一個clone()方法,可以將一個Java對象復制一份。因此在Java中可以直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。
需要注意的是能夠實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持被復制。如果一個類沒有實現這個接口但是調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。如下代碼所示:
```
class ConcretePrototype implements Cloneable
{
……
public Prototype clone()
{
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException exception) {
System.err.println("Not support cloneable");
}
return (Prototype )object;
}
……
}
```
在客戶端創建原型對象和克隆對象也很簡單,如下代碼所示:
```
Prototype obj1 = new ConcretePrototype();
Prototype obj2 = obj1.clone();
```
一般而言,Java語言中的clone()方法滿足:
(1) 對任何對象x,都有x.clone() != x,即克隆對象與原型對象不是同一個對象;
(2) 對任何對象x,都有x.clone().getClass() == x.getClass(),即克隆對象與原型對象的類型一樣;
(3) 如果對象x的equals()方法定義恰當,那么x.clone().equals(x)應該成立。
為了獲取對象的一份拷貝,我們可以直接利用Object類的clone()方法,具體步驟如下:
(1) 在派生類中覆蓋基類的clone()方法,并聲明為public;
(2) 在派生類的clone()方法中,調用super.clone();
(3)派生類需實現Cloneable接口。
此時,Object類相當于抽象原型類,所有實現了Cloneable接口的類相當于具體原型類。
- Introduction
- 基礎知識
- 設計模式概述
- 從招式與內功談起——設計模式概述(一)
- 從招式與內功談起——設計模式概述(二)
- 從招式與內功談起——設計模式概述(三)
- 面向對象設計原則
- 面向對象設計原則之單一職責原則
- 面向對象設計原則之開閉原則
- 面向對象設計原則之里氏代換原則
- 面向對象設計原則之依賴倒轉原則
- 面向對象設計原則之接口隔離原則
- 面向對象設計原則之合成復用原則
- 面向對象設計原則之迪米特法則
- 六個創建型模式
- 簡單工廠模式-Simple Factory Pattern
- 工廠三兄弟之簡單工廠模式(一)
- 工廠三兄弟之簡單工廠模式(二)
- 工廠三兄弟之簡單工廠模式(三)
- 工廠三兄弟之簡單工廠模式(四)
- 工廠方法模式-Factory Method Pattern
- 工廠三兄弟之工廠方法模式(一)
- 工廠三兄弟之工廠方法模式(二)
- 工廠三兄弟之工廠方法模式(三)
- 工廠三兄弟之工廠方法模式(四)
- 抽象工廠模式-Abstract Factory Pattern
- 工廠三兄弟之抽象工廠模式(一)
- 工廠三兄弟之抽象工廠模式(二)
- 工廠三兄弟之抽象工廠模式(三)
- 工廠三兄弟之抽象工廠模式(四)
- 工廠三兄弟之抽象工廠模式(五)
- 單例模式-Singleton Pattern
- 確保對象的唯一性——單例模式 (一)
- 確保對象的唯一性——單例模式 (二)
- 確保對象的唯一性——單例模式 (三)
- 確保對象的唯一性——單例模式 (四)
- 確保對象的唯一性——單例模式 (五)
- 原型模式-Prototype Pattern
- 對象的克隆——原型模式(一)
- 對象的克隆——原型模式(二)
- 對象的克隆——原型模式(三)
- 對象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 復雜對象的組裝與創建——建造者模式(一)
- 復雜對象的組裝與創建——建造者模式(二)
- 復雜對象的組裝與創建——建造者模式(三)
- 七個結構型模式
- 適配器模式-Adapter Pattern
- 不兼容結構的協調——適配器模式(一)
- 不兼容結構的協調——適配器模式(二)
- 不兼容結構的協調——適配器模式(三)
- 不兼容結構的協調——適配器模式(四)
- 橋接模式-Bridge Pattern
- 處理多維度變化——橋接模式(一)
- 處理多維度變化——橋接模式(二)
- 處理多維度變化——橋接模式(三)
- 處理多維度變化——橋接模式(四)
- 組合模式-Composite Pattern
- 樹形結構的處理——組合模式(一)
- 樹形結構的處理——組合模式(二)
- 樹形結構的處理——組合模式(三)
- 樹形結構的處理——組合模式(四)
- 樹形結構的處理——組合模式(五)
- 裝飾模式-Decorator Pattern
- 擴展系統功能——裝飾模式(一)
- 擴展系統功能——裝飾模式(二)
- 擴展系統功能——裝飾模式(三)
- 擴展系統功能——裝飾模式(四)
- 外觀模式-Facade Pattern
- 深入淺出外觀模式(一)
- 深入淺出外觀模式(二)
- 深入淺出外觀模式(三)
- 享元模式-Flyweight Pattern
- 實現對象的復用——享元模式(一)
- 實現對象的復用——享元模式(二)
- 實現對象的復用——享元模式(三)
- 實現對象的復用——享元模式(四)
- 實現對象的復用——享元模式(五)
- 代理模式-Proxy Pattern
- 設計模式之代理模式(一)
- 設計模式之代理模式(二)
- 設計模式之代理模式(三)
- 設計模式之代理模式(四)
- 十一個行為型模式
- 職責鏈模式-Chain of Responsibility Pattern
- 請求的鏈式處理——職責鏈模式(一)
- 請求的鏈式處理——職責鏈模式(二)
- 請求的鏈式處理——職責鏈模式(三)
- 請求的鏈式處理——職責鏈模式(四)
- 命令模式-Command Pattern
- 請求發送者與接收者解耦——命令模式(一)
- 請求發送者與接收者解耦——命令模式(二)
- 請求發送者與接收者解耦——命令模式(三)
- 請求發送者與接收者解耦——命令模式(四)
- 請求發送者與接收者解耦——命令模式(五)
- 請求發送者與接收者解耦——命令模式(六)
- 解釋器模式-Interpreter Pattern
- 自定義語言的實現——解釋器模式(一)
- 自定義語言的實現——解釋器模式(二)
- 自定義語言的實現——解釋器模式(三)
- 自定義語言的實現——解釋器模式(四)
- 自定義語言的實現——解釋器模式(五)
- 自定義語言的實現——解釋器模式(六)
- 迭代器模式-Iterator Pattern
- 遍歷聚合對象中的元素——迭代器模式(一)
- 遍歷聚合對象中的元素——迭代器模式(二)
- 遍歷聚合對象中的元素——迭代器模式(三)
- 遍歷聚合對象中的元素——迭代器模式(四)
- 遍歷聚合對象中的元素——迭代器模式(五)
- 遍歷聚合對象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 協調多個對象之間的交互——中介者模式(一)
- 協調多個對象之間的交互——中介者模式(二)
- 協調多個對象之間的交互——中介者模式(三)
- 協調多個對象之間的交互——中介者模式(四)
- 協調多個對象之間的交互——中介者模式(五)
- 備忘錄模式-Memento Pattern
- 撤銷功能的實現——備忘錄模式(一)
- 撤銷功能的實現——備忘錄模式(二)
- 撤銷功能的實現——備忘錄模式(三)
- 撤銷功能的實現——備忘錄模式(四)
- 撤銷功能的實現——備忘錄模式(五)
- 觀察者模式-Observer Pattern
- 對象間的聯動——觀察者模式(一)
- 對象間的聯動——觀察者模式(二)
- 對象間的聯動——觀察者模式(三)
- 對象間的聯動——觀察者模式(四)
- 對象間的聯動——觀察者模式(五)
- 對象間的聯動——觀察者模式(六)
- 狀態模式-State Pattern
- 處理對象的多種狀態及其相互轉換——狀態模式(一)
- 處理對象的多種狀態及其相互轉換——狀態模式(二)
- 處理對象的多種狀態及其相互轉換——狀態模式(三)
- 處理對象的多種狀態及其相互轉換——狀態模式(四)
- 處理對象的多種狀態及其相互轉換——狀態模式(五)
- 處理對象的多種狀態及其相互轉換——狀態模式(六)
- 策略模式-Strategy Pattern
- 算法的封裝與切換——策略模式(一)
- 算法的封裝與切換——策略模式(二)
- 算法的封裝與切換——策略模式(三)
- 算法的封裝與切換——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 訪問者模式-Visitor Pattern
- 操作復雜對象結構——訪問者模式(一)
- 操作復雜對象結構——訪問者模式(二)
- 操作復雜對象結構——訪問者模式(三)
- 操作復雜對象結構——訪問者模式(四)
- 設計模式趣味學習(復習)
- 設計模式與足球(一)
- 設計模式與足球(二)
- 設計模式與足球(三)
- 設計模式與足球(四)
- 設計模式綜合應用實例
- 多人聯機射擊游戲
- 多人聯機射擊游戲中的設計模式應用(一)
- 多人聯機射擊游戲中的設計模式應用(二)
- 數據庫同步系統
- 設計模式綜合實例分析之數據庫同步系統(一)
- 設計模式綜合實例分析之數據庫同步系統(二)
- 設計模式綜合實例分析之數據庫同步系統(三)