|
本帖介紹 Prototype Pattern (原型模式),并以一個(gè)「人事招聘程序」作為示例來說明。
--------------------------------------------------------
本帖的示例下載點(diǎn):
http://files.cnblogs.com/WizardWu/090713.zip
第一個(gè)示例為 Console Mode (控制臺(tái)應(yīng)用程序) 項(xiàng)目,第二個(gè)示例為 ASP.NET 網(wǎng)站項(xiàng)目。
執(zhí)行示例需要 Visual Studio 2008 或 IIS + .NET 3.0,不需要數(shù)據(jù)庫。
--------------------------------------------------------
Prototype Pattern (原型模式)
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
- Design Patterns: Elements of Reusable Object-Oriented Software
原型模式就是以一個(gè)既有的原型實(shí)例當(dāng)作范本,利用復(fù)制的方式,動(dòng)態(tài)獲得這個(gè)原型實(shí)例的狀態(tài)以及全部的字段和屬性,以此創(chuàng)建一或多個(gè)相同的對(duì)象,而且不需要知道任何創(chuàng)建的細(xì)節(jié)。
Prototype 模式打個(gè)通俗的比方:假如您在圖書館看到幾本自己喜歡的書籍,當(dāng)看到某些知識(shí)點(diǎn)時(shí),想在上面作相關(guān)記號(hào),但由于其是圖書館的書,不能在上面亂涂亂畫。此時(shí)您只好把相關(guān)的章節(jié),用復(fù)印機(jī)把它復(fù)印出來,然后在自己復(fù)印的紙張上作記號(hào)。在 Prototype Pattern 里,Clone 方法就如同此種復(fù)印的動(dòng)作,用戶從一個(gè)既有的原型實(shí)例 (如同圖書館里的書),復(fù)印后得到一或多個(gè)新的拷貝,不會(huì)破壞原本的原型,且用戶不必知道原型的內(nèi)容和格式。
Prototype 也具有一種「展示」的意味,就像是車展上的「原型」車款。當(dāng)您對(duì)某個(gè)車款感興趣時(shí),您可購買相同的車款,而不是車展上展示的那輛車。
在軟件設(shè)計(jì)方面,也常需要進(jìn)行此種對(duì)象復(fù)制。例如我們要寫一套室內(nèi)設(shè)計(jì)軟件,軟件的操作界面上有一條 Toolbar,用戶只要單擊 Toolbar 上的 Button,或用鼠標(biāo)拖曳到設(shè)計(jì)窗格中,就可創(chuàng)建一個(gè)桌子或椅子的副本,并可事后改變它的顏色或位置。當(dāng)設(shè)計(jì)師改變?cè)O(shè)計(jì)圖中的副本對(duì)象時(shí),Toolbar 上的「原型」對(duì)象并不會(huì)跟著被改變。同樣的觀念,亦適用于工業(yè)設(shè)計(jì) CAD 軟件、圖像處理軟件,以及 Visual Studio 等各種軟件的設(shè)計(jì)。
Prototype 模式的重點(diǎn)在于 Clone 方法,它負(fù)責(zé)復(fù)制 (克隆) 出一個(gè)新的對(duì)象并返回,而不是用 new 運(yùn)算符和某個(gè)類的構(gòu)造函數(shù)去創(chuàng)建實(shí)例。在此模式中,派生類如何覆寫父類的 Clone 方法將是重點(diǎn)。而 clone 的方式,又可分為「淺拷貝 (shallow copy)」和「深拷貝 (deep copy)」,在介紹這個(gè) Prototype 模式之前,先簡單介紹一下這兩種拷貝方式的差異 [1], [2], [3], [4] :
- 淺拷貝; 淺表復(fù)制 (shallow copy):對(duì)象拷貝時(shí),如果字段是「值類型 (Value Type)」,則直接復(fù)制其值 (亦即復(fù)制整個(gè)字段);若字段為「引用類型 (Reference Type)」,則只復(fù)制其「引用 (reference; pointer)」,但不復(fù)制引用的字段,亦即若更改了任一個(gè)副本對(duì)象的某一個(gè)「引用類型」字段,則原型正本對(duì)象、其他副本對(duì)象,也全部會(huì)一并更改 (如同本帖的第三個(gè)示例 02_Employee / 02_ShallowCopy_fail.ASPx.cs),也就是說正本和所有的副本,都指向了內(nèi)存的同一個(gè)位置。
- 深拷貝; 深層復(fù)制 (deep copy):不論對(duì)象的字段為「值類型」或「引用類型」,都會(huì)完整地復(fù)制,而且這些字段和屬性都是完全獨(dú)立的。在深拷貝中,所有的對(duì)象都是重復(fù)的。
另補(bǔ)充,.NET 的類型系統(tǒng),分為「值類型」、「引用類型」兩種,其對(duì)象在內(nèi)存中的存儲(chǔ)方式不同,如下:
- 值類型:只需要一段單獨(dú)的內(nèi)存,用于存儲(chǔ)實(shí)際的數(shù)據(jù)在「棧 (Stack)」里,例如:int、byte、float、double、bool、struct、enum、char、...等類型。
- 引用類型:需要兩段內(nèi)存,第一段存儲(chǔ)實(shí)際的數(shù)據(jù),其總是位于「堆 (Heap)」中;第二段是一個(gè)存在「?!估锏囊?(reference; pointer),其指向數(shù)據(jù)在「堆」中的實(shí)際存放位置,例如:object、string、class (包括自定義類)、interface、delegate、array (參考本帖的第三、第四個(gè)示例) 等類型。
但 string (字符串) 較特殊。string 雖然是「引用類型」,但卻擁有「值類型」的特性。在 Prototype Pattern 及本帖的四個(gè)示例中,當(dāng)透過 MemberwiseClone 方法做「淺拷貝」時(shí),對(duì)象的 string 字段仍會(huì)被完整地復(fù)制,其結(jié)果就如同 int 等「值類型」的字段一樣。
如下圖 1 及下方示例 01_Shell,我們可透過自定義的 Prototype 抽象類,搭配 .NET 最頂層基類 System.Object 的 MemberwiseClone 方法,達(dá)成對(duì)象的「淺拷貝」,亦即復(fù)制某個(gè)對(duì)象其所有「字段 (field)」的值;但在 .NET 中,亦可舍棄此一自定義抽象類,讓圖 1 中的 ConcretePrototype1 類、ConcretePrototype2 類,改為實(shí)現(xiàn) .NET 原生的 System.ICloneable 接口,透過實(shí)現(xiàn)此接口唯一的一個(gè) Clone 方法,來達(dá)成對(duì)象的「淺拷貝」或「深拷貝」。
圖 1 此圖為 Prototype 模式的經(jīng)典類圖

NET技術(shù):C# Design Patterns (5) - Prototype,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。