物件的克隆——原型模式(一)
張紀中版《西遊記》以出乎意料的造型和雷人的臺詞遭到廣大觀眾朋友的熱議,我們在此對該話題不作過多討論。但無論是哪個版本的《西遊記》,孫悟空都是其中的一號雄性主角,關於他(或它)拔毛變小猴的故事幾乎人人皆知,孫悟空可以用猴毛根據自己的形象,複製(又稱“克隆”或“拷貝”)出很多跟自己長得一模一樣的“身外身”來。在設計模式中也存在一個類似的模式,可以通過一個原型物件克隆出多個一模一樣的物件,該模式稱之為原型模式。
7.1 大同小異的工作週報
Sunny軟體公司一直使用自行開發的一套OA (Office Automatic,辦公自動化)系統進行日常工作辦理,但在使用過程中,越來越多的人對工作週報的建立和編寫模組產生了抱怨。追其原因,Sunny 圖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; } } |
|
在客戶類中我們只需要建立一個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介面的類相當於具體原型類。