原型模式(Prototype)與Delphi對象克隆技術(shù).doc_第1頁
原型模式(Prototype)與Delphi對象克隆技術(shù).doc_第2頁
原型模式(Prototype)與Delphi對象克隆技術(shù).doc_第3頁
原型模式(Prototype)與Delphi對象克隆技術(shù).doc_第4頁
原型模式(Prototype)與Delphi對象克隆技術(shù).doc_第5頁
已閱讀5頁,還剩20頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

原型模式(Prototype)與Delphi對象克隆技術(shù)概述:在這篇文件中,講述原型模式定義、結(jié)構(gòu)、應(yīng)用、實(shí)現(xiàn)。深入討論了Delphi中原型模式的實(shí)現(xiàn)方法。特別研究了原型模式的核心技術(shù)對象克隆技術(shù)在Delphi中的實(shí)現(xiàn)途徑。本文還探討了對象引用復(fù)制和對象克隆的區(qū)別,以及VCL的持久對象機(jī)制、對象流化技術(shù)。1、原型模式解說原型模式通過給出一個原型對象來指明所要創(chuàng)建對象的類型,然后克隆該原型對象以便創(chuàng)建出更多同類型的新對象。例如:在Delphi的IDE中,我們?yōu)樵O(shè)計(jì)窗體拖放了一個按鈕對象。為了快速創(chuàng)建更多的同樣字體和尺寸的按鈕對象,我們可以復(fù)制該按鈕(使用菜單Copy菜單或快捷鍵Ctrl+C),并在設(shè)計(jì)窗體多次粘貼(使用菜單Paste菜單或快捷鍵Ctrl+V。設(shè)計(jì)窗體中的按鈕對象是用于我們應(yīng)用程序的,而IDE中提供的按鈕對象創(chuàng)建方法(復(fù)制和粘貼)則是屬于Delphi架構(gòu)的。我們通過復(fù)制創(chuàng)建一個按鈕對象時,不需要知道Delphi是如何實(shí)現(xiàn)的。所要說明的是,雖然我們使用的是類似文字處理中的復(fù)制和粘貼,但復(fù)制的決不是一個按鈕對象的外觀(字體和尺寸等),而是整個按鈕對象,包括它的屬性和方法。所以,更嚴(yán)格講,我們是克隆了這個對象,即得到一個和源對象一樣的新對象。我們稱這種被克隆的對象(比如按鈕)為原型。只要系統(tǒng)支持克隆功能,我們就可以任意克隆對象。由此可見,原型模式適用于系統(tǒng)應(yīng)該與其對象的創(chuàng)建、組合及顯示時無關(guān)的情況,包括:當(dāng)要實(shí)例化的類是在運(yùn)行時刻指定時,例如,通過動態(tài)載入。當(dāng)類實(shí)例只是少數(shù)不同組合狀態(tài)其中之一時,這時比較好的方式在適當(dāng)?shù)臓顟B(tài)下使用一些組合的原型并復(fù)制他們,而不是人工的繼承這些類。避免建立工廠類等級結(jié)構(gòu)平行產(chǎn)出類等級結(jié)構(gòu)時。假設(shè)一個系統(tǒng)的產(chǎn)品類是動態(tài)加載的,而且產(chǎn)品類具有一定的等極結(jié)構(gòu)。這個時候如果采取工廠模式的話,工廠類就不得不具有一個相應(yīng)的等級。而產(chǎn)品類的等級結(jié)構(gòu)一旦變化,工廠類的等級結(jié)構(gòu)就不得不有一個相應(yīng)的變化。這對于產(chǎn)品結(jié)構(gòu)可能會有經(jīng)常性變化的系統(tǒng)來說,采用工廠模式就有不方便之處。這時如果采取原型模式,給每一個產(chǎn)品類配備一個克隆方法(大多數(shù)的時候只需給產(chǎn)品類等級結(jié)構(gòu)的基類配備一個克隆方法),便可以避免使用工廠模式所帶來的具有固定等級結(jié)構(gòu)的工廠類。這樣,一個使用了原型模式的系統(tǒng)與它的產(chǎn)品對象是如何創(chuàng)建出來的,以及這些產(chǎn)品對象之間的結(jié)構(gòu)是怎樣的,還有這個結(jié)構(gòu)會不會發(fā)生變化,都是沒有關(guān)系的。2、Delphi對象的克隆原型模式通過克隆原型對象來創(chuàng)建新對象,因此了解和掌握Delphi中對象的克隆是使用原型模式的關(guān)鍵。在Delphi創(chuàng)建一個對象實(shí)際上就是把一個類進(jìn)行實(shí)例化。例如要從TMan類創(chuàng)建一個名為Tom的對象,可以這樣創(chuàng)建:var Tom:TMan;.Tom:=TMan.Create;以上語句完成了以下工作:聲明TMan類型的變量Tom;為TMan類創(chuàng)建一個實(shí)例;將變量Tom指向創(chuàng)建的實(shí)例。我們從中可以發(fā)現(xiàn),對象變量和對象并不是一回事。對象是TMan類創(chuàng)建的一個實(shí)例,對象變量是該對象的引用。為了簡單,在稱呼上我們通常并不嚴(yán)格區(qū)分。但在使用時,務(wù)必分清對象引用和實(shí)際對象。有時在使用對象時無需使用對象變量來區(qū)分某一對象,例如:Factory.MakeTool(TMan.Create);這里無需區(qū)分TMan的實(shí)例是Tom還是Jack。但我們使用以下例子時,表示Tom和Jack分別引用了不同的TMan的實(shí)例,此時他們是兩個對象。var Tom,Jack:TMan;.Tom:=TMan.Create;Jack:=TMan.Create;但是如果接著使用以下語句:Tom:=Jack;此時Tom變量就不再引用Tom對象,而是引用Jack對象,這就好像Tom變成了Jack的另一個名字。當(dāng)你找Tom時,找到的是Jack。所以這種方法只能復(fù)制對象的引用而不能克隆整個對象。由此我們了解到,對象是類的動態(tài)實(shí)例,對象總是被動態(tài)分配到堆上。因此一個對象引用就如同一個句柄或一個指針。但你分配一個對象引用給一個變量時,Delphi僅復(fù)制引用,而不是整個對象。在Delphi中使用一個對象的唯一方法就是使用對象引用。一個對象引用通常以一個變量的形式存在,但是也有函數(shù)或者屬性返回值的形式。Delphi中不像有的語言那樣提供了對象克隆的功能(比如:Java有Object.clone方法),所以在Delphi中實(shí)現(xiàn)對象克隆的功能需要自己編寫代碼。好在VCL的體系結(jié)構(gòu)中,TPersistent類系下的對象可以通過覆蓋Assign方法,實(shí)現(xiàn)克隆行為。TPersistent的Assign方法較常用于兩個對象屬性的復(fù)制。在Assign方法中可以完成對象屬性、方法和事件的逐個復(fù)制。Assign方法在TPersistent類中聲明為虛方法,以便允許每個派生類定義自己的復(fù)制對象方法。如果派生類沒有重寫Assign方法,則TPersistent的Assign方法會將復(fù)制動作交給源對象來進(jìn)行:procedure TPersistent.Assign(Source: TPersistent);beginif Source nil then Source.AssignTo(Self) else AssignError(nil);end;由此可見, Assign方法實(shí)際上是調(diào)用AssignedTo方法來實(shí)現(xiàn)的,因此TPersistent的Assign方法很少被派生類所重載,但AssignTo卻常被派生類根據(jù)需要重載。如果由AssignedTo方法來實(shí)現(xiàn)復(fù)制,那么必須保證源對象的類已經(jīng)重寫了AssignedTo方法,否則將拋出一個AssignError異常:procedure TPersistent.AssignError(Source: TPersistent);varSourceName: string;beginif Source nil then SourceName := Source.ClassName else SourceName := nil;raise EConvertError.CreateResFmt(SAssignError, SourceName, ClassName);end;那么在程序中是如何使用Assign方法實(shí)現(xiàn)對象克隆的呢?如果要讓對象b克隆對象a,則可以考慮以下賦值操作:.var 假設(shè)這里的TMyObject是TPersistent的派生類,并實(shí)現(xiàn)了Assign方法。比如:TFont 。a,b:TMyObject; begina:= TMyObject.create; 關(guān)于對象a的代碼. 開始克隆對象ab:= TMyObject.create;b.Assign(a);/對象b的屬性和內(nèi)容和對象a完全相同。end;由此可見,b:=a意味著b是a的引用,即兩者是同一對象。如果寫成b.Assign(a),那么b是仍然一個獨(dú)立的對象,其狀態(tài)與a相同,也就可以看成是b克隆了a。 3、結(jié)構(gòu)和用法原始模式的結(jié)構(gòu)如圖所示。它涉及到三個參與者:-抽象原型(Prototype)聲明一個接口以便克隆其本身。-具體原型(ConcretePrototype)實(shí)現(xiàn)克隆的操作以克隆本身。-客戶(Client)請求原型克隆其本身以構(gòu)建新的對象。他們之間的合作關(guān)系為客戶請求原型克隆復(fù)制其本身以構(gòu)建新的對象。原型模式的實(shí)現(xiàn)代碼模板如示例程序 9 1所示。示例程序 9 1原型模式的實(shí)現(xiàn)代碼模板unit Prototype;interfaceuses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs;type TPrototype = class (TObject) public function Clone: TObject; virtual; abstract; end; TConcretePrototype1 = class (TPrototype) public function Clone: TObject; override; end; TConcretePrototype2 = class (TPrototype) public function Clone: TObject; override; end; TClient = class (TObject) private FPrototype: TPrototype; public procedure opteration; end;implementationTClient procedure TClient.opteration(NewObj:TPrototype);var NewObj:TPrototype;begin 克隆一個對象 NewObj:=FPrototype.Clone;end;TConcretePrototype1 function TConcretePrototype1.Clone: TObject;begin克隆的實(shí)現(xiàn)代碼end;TConcretePrototype2function TConcretePrototype2.Clone: TObject;begin克隆的實(shí)現(xiàn)代碼end;end. 前面我介紹的原型模式通常用于原型對象數(shù)目較少且比較固定的情況,這種情況下原型對象的引用由客戶對象自己保存。但是,如果要創(chuàng)建的原型對象數(shù)目不是很固定,則可以采用下面介紹的注冊形式的原型模式。在這種情況下,客戶對象不用保存對原型對象的引用,而是另有原型管理器負(fù)責(zé)。我們把負(fù)責(zé)注冊原型對象的參與者稱為原型管理器(prototype manager)。原型管理者儲存原型并依據(jù)一定的鍵值(或索引值)傳回相對應(yīng)的原型,它包含了原型對象的添加、刪除等操作,并使每個原型對象相對應(yīng)一個鍵值(或索引值)??蛻舳丝梢栽谶\(yùn)行期改變或者瀏覽這個注冊項(xiàng)。這樣一來,客戶端在系統(tǒng)中管理及擴(kuò)充原型對象數(shù)量都無須撰寫更多的程序代碼。Delphi的TObjectList類可以幫我們實(shí)現(xiàn)原型對象的注冊和管理。注冊形式的原型模式結(jié)構(gòu)圖比前圖 多了一個原型管理器(TPrototypeManager)。原型管理器負(fù)責(zé)創(chuàng)建具體原型類的對象,并記錄每一個被創(chuàng)建的對象。 示例程序 9 2給出了一個示意性實(shí)現(xiàn)的源代碼。首先,抽象原型TPrototype聲明了一個Clone方法,然后由具體原型TConcretePrototype1 和TConcretePrototype2分別實(shí)現(xiàn)該Clone方法。管理器TPrototypeManager為注冊原型對象提供必要的方法,包括:新增、獲取、計(jì)數(shù)等。它實(shí)際上是通過Delphi的TObjectList自動實(shí)現(xiàn)這些功能的。系統(tǒng)中的客戶對象通常先創(chuàng)建一個新的原型對象,然后克隆一份,注冊并保存在原型管理器中。在注冊形式的原型模式中,當(dāng)客戶對象克隆一個原型對象之前,客戶對象先查看管理對象中是否已經(jīng)存在有滿足要求的原型對象。如果有就直接從管理對象中取得該對象的引用;如果沒有,則克隆并注冊該原型對象。示例程序 9 2注冊形式的原型模式實(shí)現(xiàn)代碼模板unit Prototype2;interfaceuses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,Contnrs;type TPrototype = class (TObject) public function Clone: TObject; virtual; abstract; end; TConcretePrototype1 = class (TPrototype) public function Clone: TObject; override; end; TConcretePrototype2 = class (TPrototype) public function Clone: TObject; override; end; TPrototypeManager = class (TObject) private FObjects: TObjectList; public constructor create; override; procedure add(AObject: TPrototype); function Count: Integer; function get(Index: Integer): TPrototype; end; TClient = class (TObject) private FPrototype: TPrototype; FPrototypeManager: TPrototypeManager; public constructor create; override; procedure opteration; procedure registerPrototype; end;implementationTClient procedure TClient.opteration;begin/具體實(shí)現(xiàn)代碼end;constructor TClient.create;begin FPrototypeManager:=TPrototypeManager.Create;end;procedure TClient.registerPrototype;var ClonedObj:TPrototype; i:integer;begin /示意性代碼 FPrototype:=TConcretePrototype1.Create; ClonedObj:=TPrototype(FPrototype.Clone); FPrototypeManager.add(ClonedObj);end;TConcretePrototype1 function TConcretePrototype1.Clone: TObject;begin/具體實(shí)現(xiàn)代碼end;TConcretePrototype2function TConcretePrototype2.Clone: TObject;begin/具體實(shí)現(xiàn)代碼end;TPrototypeManagerconstructor TPrototypeManager.create;begin FObjects.Create;end;procedure TPrototypeManager.add(AObject: TPrototype);begin FObjects.Add(AObject);end;function TPrototypeManager.Count: Integer;begin result:=FObjects.Count;end;function TPrototypeManager.get(Index: Integer): TPrototype;begin result:=TPrototype(FObjects.ItemsIndex);end;end. 4、原型模式范例詳解在許多程序中我們需要允許用戶自己定義他們喜歡的字體。幾乎所有Delphi的用戶界面控件(UI)都提供TFont屬性,用于設(shè)置字體。另外Delphi還提供字體對話框方便可視化操作。通常在需要允許用戶設(shè)置字體的地方,程序員可以寫上這樣一段代碼: if FontDialog1.Execute then Memo1.Font.Assign(FontDialog1.Font);如果在程序中有很多地方需要用戶設(shè)置字體,這種代碼就會出現(xiàn)在多處,并涉及到具體的用戶界面控件,修改維護(hù)都很麻煩,而且用戶也要費(fèi)勁設(shè)置很多次字體。能不能讓用戶只設(shè)置一次字體,然后復(fù)制到他們需要的各處?也就是說能不能先創(chuàng)建一個與使用字體界面無關(guān)的字體對象原型,然后在需要的地方克???我們不妨就用原型模式來解決這個問題。按照原型模式我設(shè)計(jì)的類圖如圖所示。演示界面TfrmClient是客戶,它使用抽象類TPrototype_Font提供的SetFont方法設(shè)置字體(原型對象)以及Clone函數(shù)克隆并返回克隆好的字體對象。但抽象類TPrototype_Font作為抽象原型僅僅是一個接口,真正實(shí)現(xiàn)SetFont方法和Clone函數(shù)的是具體原型TPrototype_Font1和TPrototype_Font2。通過派生,TPrototype_Font1類和TPrototype_Font2類可以提供不同的克隆產(chǎn)品和克隆實(shí)現(xiàn)方法。范例程序中我重點(diǎn)演示不同的克隆實(shí)現(xiàn)方法。 示例程序 9 3是原型字體程序的實(shí)現(xiàn)源碼。在該程序中可以看到,我在TPrototype_Font1的Clone函數(shù)中調(diào)用了Assign方法來實(shí)現(xiàn)的字體克隆。TFont實(shí)現(xiàn)了TPersistent的虛方法Assign,作為TFont派生類的TPrototype_Font1理所當(dāng)然繼承TFont的Assign方法。function TPrototype_Font1.Clone: TObject;var Temp: TPrototype_Font1;begin Temp:=TPrototype_Font1.Create; Temp.Assign(self); result:=Temp;end;如果在某些類中沒有Assign方法(或沒有實(shí)現(xiàn)TPersistent的Assign方法),我們就不得不自己撰寫Clone函數(shù)了。TPrototype_Font2中的Clone函數(shù)就是自己編程實(shí)現(xiàn)克隆的示例。所謂克隆對象,實(shí)際上關(guān)鍵在復(fù)制該對象的狀態(tài),即對象的屬性值(數(shù)據(jù)成員變量的值)。因?yàn)橛型粋€類創(chuàng)建的對象之間的差異就在于這些屬性值的不同。function TPrototype_Font2.Clone: TObject;var Temp: TPrototype_Font2;begin Temp:=TPrototype_Font2.Create; Temp.Name:=self.Name; Temp.Size:=self.Size; Temp.Color:=self.Color; result:=Temp;end;但是,有時候某些屬性值并不是客戶所需要的,所以在克隆時對對象的屬性值進(jìn)行取舍是允許的。TPrototype_Font2中的Clone函數(shù)中,我只復(fù)制了影響字體外觀的3個最主要的屬性。這一取舍的后果是,克隆的新字體并不完全和原型字體一致,比如,不能反映出是否是斜體。這種克隆稱為不完全克隆。示例程序 9 4 原型字體程序的實(shí)現(xiàn)源碼unit PrototypeFont;interfaceuses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs;type TPrototype_Font = class (TFont) public function Clone: TObject; virtual; abstract; procedure SetFont; virtual; abstract; end; TPrototype_Font1 = class (TPrototype_Font) public function Clone: TObject; override; procedure SetFont; override; end; TPrototype_Font2 = class (TPrototype_Font) public function Clone: TObject; override; procedure SetFont; override; end; implementationTPrototype_Font1調(diào)用Assign方法實(shí)現(xiàn)的字體克隆。function TPrototype_Font1.Clone: TObject;var Temp: TPrototype_Font1;begin Temp:=TPrototype_Font1.Create; Temp.Assign(self); result:=Temp;end;procedure TPrototype_Font1.SetFont;var FontDialog: TFontDialog;begin FontDialog:= TFontDialog.Create(nil); try if FontDialog.Execute then TFont(self).Assign(FontDialog.Font); finally FontDialog.Free; end;end;TPrototype_Font2 通過自己編程實(shí)現(xiàn)的字體克隆。function TPrototype_Font2.Clone: TObject;var Temp: TPrototype_Font2;begin Temp:=TPrototype_Font2.Create; Temp.Name:=self.Name; Temp.Size:=self.Size; Temp.Color:=self.Color; result:=Temp;end;procedure TPrototype_Font2.SetFont;var FontDialog: TFontDialog;begin FontDialog:= TFontDialog.Create(nil); try if FontDialog.Execute then begin self.Name:=FontDialog.Font.Name; self.Size:=FontDialog.Font.Size; self.Color:=FontDialog.Font.Color; end; finally FontDialog.Free; end;end;end. 在示例程序 9 4所示的客戶程序中,我設(shè)計(jì)了一個演示界面。通過“設(shè)置字體”按鈕可以設(shè)置原型字體,然后點(diǎn)擊“-克隆”按鈕,可以將原型字體克隆到左邊的文本編輯框中。如圖所示。 注意這里使用的多態(tài)和轉(zhuǎn)型技術(shù)。TPrototype_Font的clone函數(shù)把克隆的對象向下轉(zhuǎn)型為TObject傳出,示例程序 9 4中再把返回的對象向上轉(zhuǎn)型為TPrototype_Font。因?yàn)門Font是TPrototype_Font的基類,下面的寫法是安全的。Memo1.Font:=TPrototype_Font(FPrototype_Font1.clone);示例程序 9 5 客戶程序源碼unit MainForm;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,PrototypeFont, ExtCtrls;type TForm1 = class (TForm) Memo1: TMemo; btnSet1: TButton; btnClone1: TButton; btnClone2: TButton; Memo2: TMemo; btnSet2: TButton; Bevel1: TBevel; procedure FormCreate(Sender: TObject); procedure btnClone1Click(Sender: TObject); procedure btnSet1Click(Sender: TObject); procedure btnClone2Click(Sender: TObject); procedure btnSet2Click(Sender: TObject); private FPrototype_Font1: TPrototype_Font; FPrototype_Font2: TPrototype_Font; end; var Form1: TForm1;implementation$R *.dfmprocedure TForm1.FormCreate(Sender: TObject);begin FPrototype_Font1:=TPrototype_Font1.Create; FPrototype_Font2:=TPrototype_Font2.Create; Memo1.Lines.Add( 這里演示調(diào)用Assign方法實(shí)現(xiàn)的字體克隆。) ; Memo2.Lines.Add( 這里演示通過自己編程實(shí)現(xiàn)的字體克隆。) ;end;procedure TForm1.btnClone1Click(Sender: TObject);begin Memo1.Font:=TPrototype_Font(FPrototype_Font1.clone);end;procedure TForm1.btnSet1Click(Sender: TObject);begin FPrototype_Font1.SetFont;end;procedure TForm1.btnClone2Click(Sender: TObject);var Prototype_Font2: TPrototype_Font2;begin Memo2.Font:=TPrototype_Font(FPrototype_Font2.clone);end;procedure TForm1.btnSet2Click(Sender: TObject);begin FPrototype_Font2.SetFont;end;end. 5、使用Delphi流技術(shù)克隆對象實(shí)現(xiàn)原型模式在Delphi中,流對象與流化存儲技術(shù)不僅是Delphi可視化設(shè)計(jì)實(shí)現(xiàn)的核心,它也為實(shí)現(xiàn)原型模式提供了更為方便、簡單和通用的對象克隆方法。盡管流化存儲所涉及的存儲媒介十分廣泛,但在各對象的接口上得到了統(tǒng)一,使程序的存儲操作變得十分方便、簡單,從而使程序員能站在更高層面上進(jìn)行對象存取的有關(guān)編程工作而無需考慮存儲介質(zhì)的具體差異。在VCL中。從TPersistent類開始提供了一個接口,引入了對象的可賦值性和流化(assignment and streaming capabilities)等性質(zhì)。TPersistent類是抽象類,沒有實(shí)例對象。但該類實(shí)現(xiàn)了對象公布(Published)屬性的存取,即在該類及其派生類中聲明為Published的屬性、方法和事件等可在設(shè)計(jì)期時顯示在Object Inspector窗中,能在Object Inspector中對對象的Published屬性進(jìn)行設(shè)計(jì)期的設(shè)計(jì),并可將設(shè)置的值存到窗體或數(shù)據(jù)模塊的DFM文件中。在程序運(yùn)行期,對象將被初始化為設(shè)計(jì)期所設(shè)置的狀態(tài)。例如,在Delphi窗體上設(shè)計(jì)的按鈕對象,在程序運(yùn)行期該對象將被初始化為設(shè)計(jì)期所設(shè)置的狀態(tài)。在編程中為了實(shí)現(xiàn)流化,我們需要使用TStream的派生類。TStream是所有流類的抽象基類,它繼承自TObject。它的派生類主要用于對文本、內(nèi)存、數(shù)據(jù)庫的Blob字段、數(shù)據(jù)壓縮等進(jìn)行操作。由于TStream與具體的存儲無關(guān),派生類卻與存儲媒介緊密相關(guān),因此每個子類都必須實(shí)現(xiàn)與具體存儲媒介相關(guān)的方法,如磁盤、內(nèi)存等。通過流的寫方法我們可以把對象轉(zhuǎn)化成位模式保存在內(nèi)存或文件中;同樣,通過流的讀方法我們可以把保存在內(nèi)存或文件中的位模式重新轉(zhuǎn)化成對象。通常把對象寫到流里的過程稱為串行化,而把對象從流中讀出來的過程稱為并行化。通過對象的一進(jìn)一出,實(shí)際上就實(shí)現(xiàn)了對象的克隆。需要注意的是,能夠被流化的對象必須是TPersistent的派生類。一般Delphi的組件(TComponent的派生類)都是支持流化的。用戶要使自己定義的類能夠支持流化,通常至少應(yīng)該使其繼承自TPersistent。參閱:需要進(jìn)一步了解Delphi對象流化機(jī)制以及VCL相關(guān)的類,請參閱我著的Delphi面向?qū)ο缶幊趟枷耄C(jī)械工業(yè)出版社2003年9月)。 下面我通過一個例子來演示說明如何使用流克隆對象實(shí)現(xiàn)原型模式。這個例子有點(diǎn)類似前圖所示的通過Delphi的IDE復(fù)制和粘貼按鈕對象。下圖是演示程序的運(yùn)行界面。我們點(diǎn)擊“克隆對象”按鈕,就會把一個文本框克隆到窗體上。 為此,我使用原型模式設(shè)計(jì)了下圖的類結(jié)構(gòu)。與前面原型模式示例程序不同的是,這里的TMemoPrototype類Clone方法利用了對象流化來完成對象的克隆。 function TMemoPrototype.Clone: TObject;var tmp:TMemoPrototype; TmpStream:TStream;begin WriteComponentResFile(MemoPrototype.dat,self); tmp:=TMemoPrototype(ReadComponentResFile(MemoPrototype.dat,nil); result:=tmp;end;這里我們使用了Delphi的Classes單元中兩個全局函數(shù)WriteComponentResFile和ReadComponentResFile來完成對象的串行化和并行化過程,前者將對象以位模式寫到資源文件MemoPrototype.dat中,后者從資源文件MemoPrototype.dat中讀出對象,每讀出一個對象就好像克隆出了一個對象。這兩個函數(shù)封裝了文件流(TFileStream)的操作過程,使我們編程簡單化,不過從他們的實(shí)現(xiàn)代碼中(示例程序 9 6),我們還是可以看出這一流化過程的。示例程序 9 6 WriteComponentResFile和ReadComponentResFile的實(shí)現(xiàn)代碼procedure WriteComponentResFile(const FileName: string; Instance: TComponent);var Stream: TStream;begin Stream := TFileStream.Create(FileName, fmCreate); try Stream.WriteComponentRes(Instance.ClassName, Instance); finally Stream.Free; end;end;function ReadComponentResFile(const FileName: string;Instance: TComponent): TComponent;var Stream: TStream;begin Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); try Result := Stream.ReadComponentRes(Instance); finally Stream.Free; end;end;于是,在使用克隆對象的演示窗體中,我們通過點(diǎn)擊“克隆對象”按鈕,觸發(fā)了以下按鈕事件:procedure TForm1.btnCloneClick(Sender: TObject);var tmp:TMemoPrototype;begin tmp:=TMemoPrototype(FPrototype1.clone); tmp.Name:=Memo+IntToStr(n); tmp.Parent:=self; tmp.Lines.Add(克隆文本框之+IntToStr(n); tmp.Left:=tmp.Left+30*n; tmp.Top:=tmp.Top+30*n; inc(n);end;通過克隆原型對象的方法,我們將方便地得到TMemoPrototype的實(shí)例。這些對象有著和原型對象一樣的狀態(tài),比如:相同的外觀和字體。(為了便于演示,我有意調(diào)整了他們的位置,否則窗體上的文本框會重疊在一起。)由此可見,流化對象的強(qiáng)大之處在于無需復(fù)雜的處理就可以將對象和一個二進(jìn)制流之間進(jìn)行互相轉(zhuǎn)化。這一功能可以巧妙地被我們用于對象克隆,特別是當(dāng)某些對象不提供Assign方法實(shí)現(xiàn)時。 6、Delphi對象的深克隆前面我講過,當(dāng)克隆一個對象時,如果克隆的是該對象所有的狀態(tài),即被克隆對象的所有變量都含有與原始對象相同的值,稱為完全克隆。否則,有選擇地克隆原始對象的部分狀態(tài),只能稱為不完全克隆。這是從對象克隆的廣度上看問題。如果從對象克隆的深度上看,對象克隆還可以分成淺克隆和深克隆。所謂淺克隆僅僅克隆所考慮的對象,而不深入克隆它所引用的對象。如果在克隆當(dāng)前對象時,同時也克隆了該對象所引用的對象,這就是所謂的深克隆了。在淺克隆中,被克隆的對象所引用的對象仍然是原始對象所引用的那個對象;而深克隆中,被克隆的對象所引用的對象已經(jīng)是一個新的對象。這就是說,深克隆同時也克隆了引用對象本身,而不是它的一個引用。由于深克隆間接復(fù)制了引用的對象,如果出現(xiàn)循壞引用,可能會出現(xiàn)意想不到的問題。所以當(dāng)一個類引用了不支持串行化的間接對象,或者引用含有循環(huán)結(jié)構(gòu)的時候,則不能考慮使用深克隆。淺克隆和深克隆問題也稱為淺復(fù)制和深復(fù)制(shallow copy versus deep copy)的問題,該問題的焦點(diǎn)在于是否復(fù)制實(shí)例變量,亦或只是共享該變量的引用。淺克隆是簡單也容易達(dá)成的,但是復(fù)雜結(jié)構(gòu)原型的克隆一般需要進(jìn)行深復(fù)制,因?yàn)檫@樣可以保證原型對象與被克隆的對象彼此獨(dú)立,互不影響。雖然深克隆的問題比較復(fù)雜,但使用流化對象的技巧可以方便實(shí)現(xiàn)對象的深克隆。下面我給出一個演示性的例子。下圖 是這個原型模式深克隆演示程序的類圖。從圖中看出,TStreamableClass包含了FContainedClass和FMemo兩個數(shù)據(jù)成員變量,分別引用了TContainedClass 和TMemoPrototype的實(shí)例對象。TStreamableClass的Clone方法實(shí)現(xiàn)了對象的深克隆。 示例程序 9 7是PrototypeByStream單元程序的源碼,這里實(shí)現(xiàn)了原型模式。需要注意的是,聲明對象時,只有放置在published域的數(shù)據(jù)成員(或?qū)傩裕┎拍軌虮蛔詣恿骰?。也就是說,要克隆的引用對象變量要么直接放在published域(如TStreamableClass 的數(shù)據(jù)成員FMemo),要么將訪問其的屬性放在published域(如TStreamableClass 的屬性ContainedClass)。從流中讀出對象時,需要將該對象轉(zhuǎn)型為原來的類型,比如:AClassInstance := TStreamableClass( ReadComponentResFile(DeepClone, nil);但是事先要使用RegisterClass 或RegisterClasses注冊自己的類,比如: RegisterClasses(TMemoPrototype,TContainedClass, TStreamableClass);否則會找不到類,出現(xiàn)EClassNotFound異常。示例程序 9 7 PrototypeByStream單元程序源碼unit PrototypeByStream;interfaceuses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls;type TMemoPrototype = class (TMemo) public constructor create(AOwner:TComponent);override; published function Clone: TObject; end; TContainedClass = class(TPersistent) private FSomeData: Integer; procedure SetSomeData(Value: Integer); public constructor Create; publishedproperty SomeData: Integer read FSomeData write SetSomeData; end; TStreamableClass = class(TComponent) private FContainedClass: TContainedClass; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published FMemo:TMemoPrototype; function Clone: TObject;property ContainedClass: TContainedClass read FContainedClass write FContainedClass; end;implementationprocedure TContainedClass.SetSomeData(Value: Integer);begin FSomeData := Value;end;constructor TContainedClass.Create;begin FSomeData := 42;end;constructor TStreamableClass.Create(AOwner: TComponent);begin inherited Create(AOwner); FContainedClass := TContainedClass.Create; FMemo:=TMemoPrototype.create(AOwner);end;destructor TStreamableClass.Destroy;begin FContainedClass.Free;end;function TStreamableClass.Clone: TObject;var AClassInstance: TStreamableClass;begin AClassInstance := TStreamableClass.Create(nil); WriteComponentResFile(DeepClone, AClassInstance); FreeAndNil(AClassInstance);AClassInstance := TStreamableClass( ReadComponentResFile(DeepClone, nil); result:=AClassInstance;end;TMemoPrototypefunction TMemoPrototyp

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論