鍍金池/ 教程/ Java/ 對(duì)象的克隆——原型模式(一)
工廠三兄弟之抽象工廠模式(五)
復(fù)雜對(duì)象的組裝與創(chuàng)建——建造者模式(一)
工廠三兄弟之工廠方法模式(一)
復(fù)雜對(duì)象的組裝與創(chuàng)建——建造者模式(二)
確保對(duì)象的唯一性——單例模式 (二)
工廠三兄弟之簡(jiǎn)單工廠模式(四)
確保對(duì)象的唯一性——單例模式 (一)
工廠三兄弟之工廠方法模式(四)
對(duì)象的克隆——原型模式(一)
工廠三兄弟之抽象工廠模式(二)
工廠三兄弟之工廠方法模式(三)
工廠三兄弟之抽象工廠模式(一)
工廠三兄弟之抽象工廠模式(四)
確保對(duì)象的唯一性——單例模式 (三)
工廠三兄弟之簡(jiǎn)單工廠模式(三)
對(duì)象的克隆——原型模式(二)
復(fù)雜對(duì)象的組裝與創(chuàng)建——建造者模式(三)
對(duì)象的克隆——原型模式(四)
確保對(duì)象的唯一性——單例模式(四)
工廠三兄弟之簡(jiǎn)單工廠模式(一)
工廠三兄弟之簡(jiǎn)單工廠模式(二)
對(duì)象的克隆——原型模式(三)
工廠三兄弟之抽象工廠模式(三)
確保對(duì)象的唯一性——單例模式(五)
工廠三兄弟之工廠方法模式(二)

對(duì)象的克隆——原型模式(一)

張紀(jì)中版《西游記》以出乎意料的造型和雷人的臺(tái)詞遭到廣大觀眾朋友的熱議,我們?cè)诖藢?duì)該話題不作過(guò)多討論。但無(wú)論是哪個(gè)版本的《西游記》,孫悟空都是其中的一號(hào)雄性主角,關(guān)于他(或它)拔毛變小猴的故事幾乎人人皆知,孫悟空可以用猴毛根據(jù)自己的形象,復(fù)制(又稱“克隆”或“拷貝”)出很多跟自己長(zhǎng)得一模一樣的“身外身”來(lái)。在設(shè)計(jì)模式中也存在一個(gè)類似的模式,可以通過(guò)一個(gè)原型對(duì)象克隆出多個(gè)一模一樣的對(duì)象,該模式稱之為原型模式。

大同小異的工作周報(bào)

Sunny 軟件公司一直使用自行開(kāi)發(fā)的一套 OA (Office Automatic,辦公自動(dòng)化)系統(tǒng)進(jìn)行日常工作辦理,但在使用過(guò)程中,越來(lái)越多的人對(duì)工作周報(bào)的創(chuàng)建和編寫(xiě)模塊產(chǎn)生了抱怨。追其原因,Sunny 軟件公司的 OA 管理員發(fā)現(xiàn),由于某些崗位每周工作存在重復(fù)性,工作周報(bào)內(nèi)容都大同小異,如圖7-1工作周報(bào)示意圖。這些周報(bào)只有一些小地方存在差異,但是現(xiàn)行系統(tǒng)每周默認(rèn)創(chuàng)建的周報(bào)都是空白報(bào)表,用戶只能通過(guò)重新輸入或不斷復(fù)制粘貼來(lái)填寫(xiě)重復(fù)的周報(bào)內(nèi)容,極大降低了工作效率,浪費(fèi)寶貴的時(shí)間。如何快速創(chuàng)建相同或者相似的工作周報(bào),成為 Sunny 公司 OA 開(kāi)發(fā)人員面臨的一個(gè)新問(wèn)題。

工作周報(bào)示意圖

Sunny公司的開(kāi)發(fā)人員通過(guò)對(duì)問(wèn)題進(jìn)行仔細(xì)分析,決定按照如下思路對(duì)工作周報(bào)模塊進(jìn)行重新設(shè)計(jì)和實(shí)現(xiàn):

(1)除了允許用戶創(chuàng)建新周報(bào)外,還允許用戶將創(chuàng)建好的周報(bào)保存為模板;

(2)用戶在再次創(chuàng)建周報(bào)時(shí),可以創(chuàng)建全新的周報(bào),還可以選擇合適的模板復(fù)制生成一份相同的周報(bào),然后對(duì)新生成的周報(bào)根據(jù)實(shí)際情況進(jìn)行修改,產(chǎn)生新的周報(bào)。

只要按照如上兩個(gè)步驟進(jìn)行處理,工作周報(bào)的創(chuàng)建效率將得以大大提高。這個(gè)過(guò)程讓我們想到平時(shí)經(jīng)常進(jìn)行的兩個(gè)電腦基本操作:復(fù)制和粘貼,快捷鍵通常為 Ctrl + C 和 Ctrl + V,通過(guò)對(duì)已有對(duì)象的復(fù)制和粘貼,我們可以創(chuàng)建大量的相同對(duì)象。如何在一個(gè)面向?qū)ο笙到y(tǒng)中實(shí)現(xiàn)對(duì)象的復(fù)制和粘貼呢?不用著急,本章我們介紹的原型模式正為解決此類問(wèn)題而誕生。

原型模式概述

在使用原型模式時(shí),我們需要首先創(chuàng)建一個(gè)原型對(duì)象,再通過(guò)復(fù)制這個(gè)原型對(duì)象來(lái)創(chuàng)建更多同類型的對(duì)象。試想,如果連孫悟空的模樣都不知道,怎么拔毛變小猴子呢?原型模式的定義如下:

原型模式(Prototype Pattern):使用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象。原型模式是一種對(duì)象創(chuàng)建型模式。

原型模式的工作原理很簡(jiǎn)單:將一個(gè)原型對(duì)象傳給那個(gè)要發(fā)動(dòng)創(chuàng)建的對(duì)象,這個(gè)要發(fā)動(dòng)創(chuàng)建的對(duì)象通過(guò)請(qǐng)求原型對(duì)象拷貝自己來(lái)實(shí)現(xiàn)創(chuàng)建過(guò)程。由于在軟件系統(tǒng)中我們經(jīng)常會(huì)遇到需要?jiǎng)?chuàng)建多個(gè)相同或者相似對(duì)象的情況,因此原型模式在真實(shí)開(kāi)發(fā)中的使用頻率還是非常高的。原型模式是一種“另類”的創(chuàng)建型模式,創(chuàng)建克隆對(duì)象的工廠就是原型類自身,工廠方法由克隆方法來(lái)實(shí)現(xiàn)。

需要注意的是通過(guò)克隆方法所創(chuàng)建的對(duì)象是全新的對(duì)象,它們?cè)趦?nèi)存中擁有新的地址,通常對(duì)克隆所產(chǎn)生的對(duì)象進(jìn)行修改對(duì)原型對(duì)象不會(huì)造成任何影響,每一個(gè)克隆對(duì)象都是相互獨(dú)立的。通過(guò)不同的方式修改可以得到一系列相似但不完全相同的對(duì)象。

原型模式的結(jié)構(gòu)如圖所示:

http://wiki.jikexueyuan.com/project/design-pattern-creation/images/1333464343_7157.gif" alt="原型模式結(jié)構(gòu)圖" />

在原型模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Prototype(抽象原型類):它是聲明克隆方法的接口,是所有具體原型類的公共父類,可以是抽象類也可以是接口,甚至還可以是具體實(shí)現(xiàn)類。

  • ConcretePrototype(具體原型類):它實(shí)現(xiàn)在抽象原型類中聲明的克隆方法,在克隆方法中返回自己的一個(gè)克隆對(duì)象。

  • Client(客戶類):讓一個(gè)原型對(duì)象克隆自身從而創(chuàng)建一個(gè)新的對(duì)象,在客戶類中只需要直接實(shí)例化或通過(guò)工廠方法等方式創(chuàng)建一個(gè)原型對(duì)象,再通過(guò)調(diào)用該對(duì)象的克隆方法即可得到多個(gè)相同的對(duì)象。由于客戶類針對(duì)抽象原型類 Prototype 編程,因此用戶可以根據(jù)需要選擇具體原型類,系統(tǒng)具有較好的可擴(kuò)展性,增加或更換具體原型類都很方便。

原型模式的核心在于如何實(shí)現(xiàn)克隆方法,下面將介紹兩種在 Java 語(yǔ)言中常用的克隆實(shí)現(xiàn)方法:

通用實(shí)現(xiàn)方法

通用的克隆實(shí)現(xiàn)方法是在具體原型類的克隆方法中實(shí)例化一個(gè)與自身類型相同的對(duì)象并將其返回,并將相關(guān)的參數(shù)傳入新創(chuàng)建的對(duì)象中,保證它們的成員屬性相同。示意代碼如下所示:

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(); //創(chuàng)建新對(duì)象
    prototype.setAttr(this.attr);
    return prototype;
}
}

思考

能否將上述代碼中的 clone() 方法寫(xiě)成:public Prototype clone() { return this; }?給出你的理由。

在客戶類中我們只需要?jiǎng)?chuàng)建一個(gè) ConcretePrototype 對(duì)象作為原型對(duì)象,然后調(diào)用其 clone() 方法即可得到對(duì)應(yīng)的克隆對(duì)象,如下代碼所示:

Prototype obj1  = new ConcretePrototype();
obj1.setAttr("Sunny");
Prototype obj2  = obj1.clone();  

這種方法可作為原型模式的通用實(shí)現(xiàn),它與編程語(yǔ)言特性無(wú)關(guān),任何面向?qū)ο笳Z(yǔ)言都可以使用這種形式來(lái)實(shí)現(xiàn)對(duì)原型的克隆。

Java 語(yǔ)言提供的 clone() 方法

學(xué)過(guò) Java 語(yǔ)言的人都知道,所有的 Java 類都繼承自 java.lang.Object。事實(shí)上,Object 類提供一個(gè) clone() 方法,可以將一個(gè) Java 對(duì)象復(fù)制一份。因此在 Java 中可以直接使用 Object 提供的 clone() 方法來(lái)實(shí)現(xiàn)對(duì)象的克隆,Java 語(yǔ)言中的原型模式實(shí)現(xiàn)很簡(jiǎn)單。

需要注意的是能夠?qū)崿F(xiàn)克隆的 Java 類必須實(shí)現(xiàn)一個(gè)標(biāo)識(shí)接口 Cloneable,表示這個(gè) Java 類支持被復(fù)制。如果一個(gè)類沒(méi)有實(shí)現(xiàn)這個(gè)接口但是調(diào)用了 clone() 方法,Java 編譯器將拋出一個(gè) 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;
}
……
}  

在客戶端創(chuàng)建原型對(duì)象和克隆對(duì)象也很簡(jiǎn)單,如下代碼所示:

Prototype obj1  = new ConcretePrototype();
Prototype obj2  = obj1.clone();  

一般而言,Java 語(yǔ)言中的 clone() 方法滿足:

(1) 對(duì)任何對(duì)象 x,都有 x.clone() != x,即克隆對(duì)象與原型對(duì)象不是同一個(gè)對(duì)象;

(2) 對(duì)任何對(duì)象 x,都有 x.clone().getClass() == x.getClass(),即克隆對(duì)象與原型對(duì)象的類型一樣;

(3) 如果對(duì)象 x 的 equals() 方法定義恰當(dāng),那么 x.clone().equals(x) 應(yīng)該成立。

為了獲取對(duì)象的一份拷貝,我們可以直接利用 Object 類的 clone() 方法,具體步驟如下:

(1) 在派生類中覆蓋基類的 clone() 方法,并聲明為 public;

(2) 在派生類的 clone() 方法中,調(diào)用 super.clone();

(3)派生類需實(shí)現(xiàn) Cloneable 接口。

此時(shí),Object 類相當(dāng)于抽象原型類,所有實(shí)現(xiàn)了 Cloneable 接口的類相當(dāng)于具體原型類。