版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、var原型模式(Prototype)與Delphi對(duì)象克隆技術(shù)概述:在這篇文件中,講述原型模式定義、結(jié)構(gòu)、應(yīng)用、實(shí)現(xiàn)。深入討論了Delphi中原型模式的實(shí)現(xiàn)方法。特別研究了原型模式的核心技術(shù)對(duì)象克隆技術(shù)在Delphi中的實(shí)現(xiàn)途徑。本文還探討了對(duì)象引用復(fù)制和對(duì)象克隆的區(qū)別,以及VCL的持久對(duì)象機(jī)制、對(duì)象流化技術(shù)。1、原型模式解說(shuō)原型模式通過(guò)給出一個(gè)原型對(duì)象來(lái)指明所要?jiǎng)?chuàng)建對(duì)象的類(lèi)型,然后克隆該原型對(duì)象以便創(chuàng)建出更多同類(lèi)型的新對(duì)象。例如:在Delphi的IDE中,我們?yōu)樵O(shè)計(jì)窗體拖放了一個(gè)按鈕對(duì)象。為了快速創(chuàng)建更多的同樣字體和尺寸的按鈕對(duì)象,我們可以復(fù)制該按鈕(使用菜單Copy菜單或快捷鍵Ctrl+C
2、),并在設(shè)計(jì)窗體多次粘貼(使用菜單Paste菜單或快捷鍵Ctrl+V。設(shè)計(jì)窗體中的按鈕對(duì)象是用于我們應(yīng)用程序的,而IDE中提供的按鈕對(duì)象創(chuàng)建方法(復(fù)制和粘貼)則是屬于Delphi架構(gòu)的。我們通過(guò)復(fù)制創(chuàng)建一個(gè)按鈕對(duì)象時(shí),不需要知道Delphi是如何實(shí)現(xiàn)的。所要說(shuō)明的是,雖然我們使用的是類(lèi)似文字處理中的復(fù)制和粘貼,但復(fù)制的決不是一個(gè)按鈕對(duì)象的外觀(字體和尺寸等),而是整個(gè)按鈕對(duì)象,包括它的屬性和方法。所以,更嚴(yán)格講,我們是克隆了這個(gè)對(duì)象,即得到一個(gè)和源對(duì)象一樣的新對(duì)象。我們稱(chēng)這種被克隆的對(duì)象(比如按鈕)為原型。只要系統(tǒng)支持克隆功能,我們就可以任意克隆對(duì)象。由此可見(jiàn),原型模式適用于系統(tǒng)應(yīng)該與其對(duì)象的
3、創(chuàng)建、組合及顯示時(shí)無(wú)關(guān)的情況,包括:當(dāng)要實(shí)例化的類(lèi)是在運(yùn)行時(shí)刻指定時(shí),例如,通過(guò)動(dòng)態(tài)載入。當(dāng)類(lèi)實(shí)例只是少數(shù)不同組合狀態(tài)其中之一時(shí),這時(shí)比較好的方式在適當(dāng)?shù)臓顟B(tài)下使用一些組合的原型并復(fù)制他們,而不是人工的繼承這些類(lèi)。避免建立工廠類(lèi)等級(jí)結(jié)構(gòu)平行產(chǎn)出類(lèi)等級(jí)結(jié)構(gòu)時(shí)。假設(shè)一個(gè)系統(tǒng)的產(chǎn)品類(lèi)是動(dòng)態(tài)加載的,而且產(chǎn)品類(lèi)具有一定的等極結(jié)構(gòu)。這個(gè)時(shí)候如果采取工廠模式的話,工廠類(lèi)就不得不具有一個(gè)相應(yīng)的等級(jí)。而產(chǎn)品類(lèi)的等級(jí)結(jié)構(gòu)一旦變化,工廠類(lèi)的等級(jí)結(jié)構(gòu)就不得不有一個(gè)相應(yīng)的變化。這對(duì)于產(chǎn)品結(jié)構(gòu)可能會(huì)有經(jīng)常性變化的系統(tǒng)來(lái)說(shuō),采用工廠模式就有不方便之處。這時(shí)如果采取原型模式,給每一個(gè)產(chǎn)品類(lèi)配備一個(gè)克隆方法(大多數(shù)的時(shí)候只需給
4、產(chǎn)品類(lèi)等級(jí)結(jié)構(gòu)的基類(lèi)配備一個(gè)克隆方法),便可以避免使用工廠模式所帶來(lái)的具有固定等級(jí)結(jié)構(gòu)的工廠類(lèi)。這樣,一個(gè)使用了原型模式的系統(tǒng)與它的產(chǎn)品對(duì)象是如何創(chuàng)建出來(lái)的,以及這些產(chǎn)品對(duì)象之間的結(jié)構(gòu)是怎樣的,還有這個(gè)結(jié)構(gòu)會(huì)不會(huì)發(fā)生變化,都是沒(méi)有關(guān)系的。2、Delphi對(duì)象的克隆原型模式通過(guò)克隆原型對(duì)象來(lái)創(chuàng)建新對(duì)象,因此了解和掌握Delphi中對(duì)象的克隆是使用原型模式的關(guān)鍵。在Delphi創(chuàng)建一個(gè)對(duì)象實(shí)際上就是把一個(gè)類(lèi)進(jìn)行實(shí)例化。例如要從TMan類(lèi)創(chuàng)建一個(gè)名為T(mén)om的對(duì)象,可以這樣創(chuàng)建:varTom:TMan;Tom:=TMan.Create;以上語(yǔ)句完成了以下工作:聲明TMan類(lèi)型的變量Tom;為T(mén)Man類(lèi)
5、創(chuàng)建一個(gè)實(shí)例;將變量Tom指向創(chuàng)建的實(shí)例。我們從中可以發(fā)現(xiàn),對(duì)象變量和對(duì)象并不是一回事。對(duì)象是TMan類(lèi)創(chuàng)建的一個(gè)實(shí)例,對(duì)象變量是該對(duì)象的引用。為了簡(jiǎn)單,在稱(chēng)呼上我們通常并不嚴(yán)格區(qū)分。但在使用時(shí),務(wù)必分清對(duì)象引用和實(shí)際對(duì)象。有時(shí)在使用對(duì)象時(shí)無(wú)需使用對(duì)象變量來(lái)區(qū)分某一對(duì)象,例如:Factory.MakeTool(TMan.Create);這里無(wú)需區(qū)分TMan的實(shí)例是Tom還是Jack。但我們使用以下例子時(shí),表示Tom和Jack分別引用了不同的TMan的實(shí)例,此時(shí)他們是兩個(gè)對(duì)象。varTom,Jack:TMan;Tom:=TMan.Create;Jack:=TMan.Create;但是如果接著使用
6、以下語(yǔ)句:Tom:=Jack;此時(shí)Tom變量就不再引用Tom對(duì)象,而是引用Jack對(duì)象,這就好像Tom變成了Jack的另一個(gè)名字。當(dāng)你找Tom時(shí),找到的是Jack。所以這種方法只能復(fù)制對(duì)象的引用而不能克隆整個(gè)對(duì)象。由此我們了解到,對(duì)象是類(lèi)的動(dòng)態(tài)實(shí)例,對(duì)象總是被動(dòng)態(tài)分配到堆上。因此一個(gè)對(duì)象引用就如同一個(gè)句柄或一個(gè)指針。但你分配一個(gè)對(duì)象引用給一個(gè)變量時(shí),Delphi僅復(fù)制引用,而不是整個(gè)對(duì)象。在Delphi中使用一個(gè)對(duì)象的唯一方法就是使用對(duì)象引用。一個(gè)對(duì)象引用通常以一個(gè)變量的形式存在,但是也有函數(shù)或者屬性返回值的形式。Delphi中不像有的語(yǔ)言那樣提供了對(duì)象克隆的功能(比如:Java有Object
7、.clone方法),所以在Delphi中實(shí)現(xiàn)對(duì)象克隆的功能需要自己編寫(xiě)代碼。好在VCL的體系結(jié)構(gòu)中,TPersistent類(lèi)系下的對(duì)象可以通過(guò)覆蓋Assign方法,實(shí)現(xiàn)克隆行為。TPersistent的Assign方法較常用于兩個(gè)對(duì)象屬性的復(fù)制。在Assign方法中可以完成對(duì)象屬性、方法和事件的逐個(gè)復(fù)制。Assign方法在TPersistent類(lèi)中聲明為虛方法,以便允許每個(gè)派生類(lèi)定義自己的復(fù)制對(duì)象方法。如果派生類(lèi)沒(méi)有重寫(xiě)Assign方法,則TPersistent的Assign方法會(huì)將復(fù)制動(dòng)作交給源對(duì)象來(lái)進(jìn)行:procedureTPersistent.Assign(Source:TPersist
8、ent);beginifSourcenilthenSource.AssignTo(Self)elseAssignError(nil);end;由此可見(jiàn),Assign方法實(shí)際上是調(diào)用AssignedTo方法來(lái)實(shí)現(xiàn)的,因此TPersistent的Assign方法很少被派生類(lèi)所重載,但AssignTo卻常被派生類(lèi)根據(jù)需要重載。如果由AssignedTo方法來(lái)實(shí)現(xiàn)復(fù)制,那么必須保證源對(duì)象的類(lèi)已經(jīng)重寫(xiě)了AssignedTo方法,否則將拋出一個(gè)AssignError異常:procedureTPersistent.AssignError(Source:TPersistent);SourceName:stri
9、ng;beginifSourcenilthenSourceName:=Source.ClassNameelseSourceName:=nil;raiseEConvertError.CreateResFmt(SAssignError,SourceName,ClassName);end;那么在程序中是如何使用Assign方法實(shí)現(xiàn)對(duì)象克隆的呢?如果要讓對(duì)象b克隆對(duì)象a,則可以考慮以下賦值操作:var假設(shè)這里的TMyObject是TPersistent的派生類(lèi)并實(shí)現(xiàn)了Assign方法。比如:TFont。a,b:TMyObject;begina:=TMyObject.create;關(guān)于對(duì)象a的代碼開(kāi)始克
10、隆對(duì)象ab:=TMyObject.create;b.Assign(a);/對(duì)象b的屬性和內(nèi)容和對(duì)象a完全相同。end;由此可見(jiàn),b:=a意味著b是a的引用,即兩者是同一對(duì)象。如果寫(xiě)成b.Assign(a),那么b是仍然一個(gè)獨(dú)立的對(duì)象,其狀態(tài)與a相同,也就可以看成是b克隆了a。3、結(jié)構(gòu)和用法原始模式的結(jié)構(gòu)如圖所示。它涉及到三個(gè)參與者:-抽象原型(Prototype)聲明一個(gè)接口以便克隆其本身。-具體原型(ConcretePrototype)實(shí)現(xiàn)克隆的操作以克隆本身。-客戶(hù)(Client)請(qǐng)求原型克隆其本身以構(gòu)建新的對(duì)象。他們之間的合作關(guān)系為客戶(hù)請(qǐng)求原型克隆復(fù)制其本身以構(gòu)建新的對(duì)象。原型模式的實(shí)現(xiàn)
11、代碼模板如示例程序91所示。示例程序91原型模式的實(shí)現(xiàn)代碼模板unitPrototype;interfaceusesSysUtils,Windows,Messages,Classes,Graphics,Controls,Forms,Dialogs;typeTPrototype=class(TObject)publicfunctionClone:TObject;virtual;abstract;end;TConcretePrototype1=class(TPrototype)publicfunctionClone:TObject;override;end;TConcretePrototype2=
12、class(TPrototype)publicfunctionClone:TObject;override;end;TClient=class(TObject)privateFPrototype:TPrototype;publicprocedureopteration;end;implementationTClientprocedureTClient.opteration(NewObj:TPrototype);NewObj:TPrototype;begin克隆一個(gè)對(duì)象NewObj:=FPrototype.Clone;end;TConcretePrototype1functionTConcret
13、ePrototype1.Clone:TObject;begin克隆的實(shí)現(xiàn)代碼end;TConcretePrototype2functionTConcretePrototype2.Clone:TObject;begin克隆的實(shí)現(xiàn)代碼end;end.前面我介紹的原型模式通常用于原型對(duì)象數(shù)目較少且比較固定的情況,這種情況下原型對(duì)象的引用由客戶(hù)對(duì)象自己保存。但是,如果要?jiǎng)?chuàng)建的原型對(duì)象數(shù)目不是很固定,則可以采用下面介紹的注冊(cè)形式的原型模式。在這種情況下,客戶(hù)對(duì)象不用保存對(duì)原型對(duì)象的引用,而是另有原型管理器負(fù)責(zé)。我們把負(fù)責(zé)注冊(cè)原型對(duì)象的參與者稱(chēng)為原型管理器(prototypemanager)。原型管理者儲(chǔ)
14、存原型并依據(jù)一定的鍵值(或索引值)傳回相對(duì)應(yīng)的原型,它包含了原型對(duì)象的添加、刪除等操作,并使每個(gè)原型對(duì)象相對(duì)應(yīng)一個(gè)鍵值(或索引值)??蛻?hù)端可以在運(yùn)行期改變或者瀏覽這個(gè)注冊(cè)項(xiàng)。這樣一來(lái),客戶(hù)端在系統(tǒng)中管理及擴(kuò)充原型對(duì)象數(shù)量都無(wú)須撰寫(xiě)更多的程序代碼。Delphi的TObjectList類(lèi)可以幫我們實(shí)現(xiàn)原型對(duì)象的注冊(cè)和管理。注冊(cè)形式的原型模式結(jié)構(gòu)圖比前圖多了一個(gè)原型管理器(TPrototypeManager)。原型管理器負(fù)責(zé)創(chuàng)建具體原型類(lèi)的對(duì)象,并記錄每一個(gè)被創(chuàng)建的對(duì)象。序92給出了一個(gè)示意性實(shí)現(xiàn)的源代碼。首先,抽象原型TPrototype聲明了一個(gè)Clone方法,然后由具體原型TConcreteP
15、rototypel和TConcretePrototype2分別實(shí)現(xiàn)該Clone方法。管理器TPrototypeManager為注冊(cè)原型對(duì)象提供必要的方法,包括:新增、獲取、計(jì)數(shù)等。它實(shí)際上是通過(guò)Delphi的TObjectList自動(dòng)實(shí)現(xiàn)這些功能的。系統(tǒng)中的客戶(hù)對(duì)象通常先創(chuàng)建一個(gè)新的原型對(duì)象,然后克隆一份,注冊(cè)并保存在原型管理器中。在注冊(cè)形式的原型模式中,當(dāng)客戶(hù)對(duì)象克隆一個(gè)原型對(duì)象之前,客戶(hù)對(duì)象先查看管理對(duì)象中是否已經(jīng)存在有滿(mǎn)足要求的原型對(duì)象。如果有就直接從管理對(duì)象中取得該對(duì)象的引用;如果沒(méi)有,則克隆并注冊(cè)該原型對(duì)象。示例程序92注冊(cè)形式的原型模式實(shí)現(xiàn)代碼模板unitPrototype2;in
16、terfaceusesSysUtils,Windows,Messages,Classes,Graphics,Controls,Forms,Dialogs,Contnrs;typeTPrototype=class(TObject)publicfunctionClone:TObject;virtual;abstract;end;TConcretePrototype1=class(TPrototype)publicfunctionClone:TObject;override;end;TConcretePrototype2=class(TPrototype)publicfunctionClone:TO
17、bject;override;end;TPrototypeManager=class(TObject)privateFObjects:TObjectList;publicconstructorcreate;override;procedureadd(AObject:TPrototype);functionCount:Integer;functionget(Index:Integer):TPrototype;end;TClient=class(TObject)privateFPrototype:TPrototype;FPrototypeManager:TPrototypeManager;publ
18、icconstructorcreate;override;procedureopteration;procedureregisterPrototype;end;implementationTClientprocedureTClient.opteration;begin/具體實(shí)現(xiàn)代碼end;constructorTClient.create;beginFPrototypeManager:=TPrototypeManager.Create;end;procedureTClient.registerPrototype;varClonedObj:TPrototype;i:integer;begin/示
19、意性代碼FPrototype:=TConcretePrototype1.Create;ClonedObj:=TPrototype(FPrototype.Clone);FPrototypeManager.add(ClonedObj);end;TConcretePrototype1functionTConcretePrototype1.Clone:TObject;begin/具體實(shí)現(xiàn)代碼end;TConcretePrototype2functionTConcretePrototype2.Clone:TObject;begin/具體實(shí)現(xiàn)代碼end;TPrototypeManagerconstruct
20、orTPrototypeManager.create;beginFObjects.Create;end;procedureTPrototypeManager.add(AObject:TPrototype);beginFObjects.Add(AObject);end;functionTPrototypeManager.Count:Integer;beginresult:=FObjects.Count;end;functionTPrototypeManager.get(Index:Integer):TPrototype;beginresult:=TPrototype(FObjects.Items
21、Index);end;end.4、原型模式范例詳解在許多程序中我們需要允許用戶(hù)自己定義他們喜歡的字體。幾乎所有Delphi的用戶(hù)界面控件(UI)都提供TFont屬性,用于設(shè)置字體。另外Delphi還提供字體對(duì)話框方便可視化操作。通常在需要允許用戶(hù)設(shè)置字體的地方,程序員可以寫(xiě)上這樣一段代碼:ifFontDialog1.ExecutethenMemo1.Font.Assign(FontDialog1.Font);如果在程序中有很多地方需要用戶(hù)設(shè)置字體,這種代碼就會(huì)出現(xiàn)在多處,并涉及到具體的用戶(hù)界面控件,修改維護(hù)都很麻煩,而且用戶(hù)也要費(fèi)勁設(shè)置很多次字體。能不能讓用戶(hù)只設(shè)置一次字體,然后復(fù)制到他們需要
22、的各處?也就是說(shuō)能不能先創(chuàng)建一個(gè)與使用字體界面無(wú)關(guān)的字體對(duì)象原型,然后在需要的地方克?。课覀儾环辆陀迷湍J絹?lái)解決這個(gè)問(wèn)題。按照原型模式我設(shè)計(jì)的類(lèi)圖如圖所示。演示界面TfrmClient是客戶(hù),它使用抽象類(lèi)TPrototype_Font提供的SetFont方法設(shè)置字體(原型對(duì)象)以及Clone函數(shù)克隆并返回克隆好的字體對(duì)象。但抽象類(lèi)TPrototype_Font作為抽象原型僅僅是一個(gè)接口,真正實(shí)現(xiàn)SetFont方法和Clone函數(shù)的是具體原型TPrototype_Font1和TPrototype_Font2。通過(guò)派生,TPrototype_Font1類(lèi)和TPrototype_Font2類(lèi)可以提
23、供不同的克隆產(chǎn)品和克隆實(shí)現(xiàn)方法。范例程序中我重點(diǎn)演示不同的克隆實(shí)現(xiàn)方法。vTfrmClientattributesoBevel1:TEevel:obtndonel.TButton:obtnQone2TButton;obtnSetl:TButton:obtrSet2;TButtwi:oJVlemol:TWenio:oMemos:TMemo:-FRnototype_Fort1:TPrototype_Font;-FFrototype_Fort2:TFrototype_Font.operationsqbtndorielClick(.)obtrjGon&2aick(.)0btnSet1OicK(.job
24、tnSet2Clid(.)oFormQeafe(示例程序93是原型字體程序的實(shí)現(xiàn)源碼。在該程序中可以看到,我在TPrototype_Fontl的Clone函數(shù)中調(diào)用了Assign方法來(lái)實(shí)現(xiàn)的字體克隆。TFont實(shí)現(xiàn)了TPersistent的虛方法Assign,作為T(mén)Font派生類(lèi)的TPrototype_Font1理所當(dāng)然繼承TFont的Assign方法。functionTPrototype_Fontl.Clone:TObject;varTemp:TPrototype_Font1;beginTemp:=TPrototype_Font1.Create;Temp.Assign(self);resul
25、t:=Temp;end;如果在某些類(lèi)中沒(méi)有Assign方法(或沒(méi)有實(shí)現(xiàn)TPersistent的Assign方法),我們就不得不自己撰寫(xiě)Clone函數(shù)了。TPrototype_Font2中的Clone函數(shù)就是自己編程實(shí)現(xiàn)克隆的示例。所謂克隆對(duì)象,實(shí)際上關(guān)鍵在復(fù)制該對(duì)象的狀態(tài),即對(duì)象的屬性值(數(shù)據(jù)成員變量的值)。因?yàn)橛型粋€(gè)類(lèi)創(chuàng)建的對(duì)象之間的差異就在于這些屬性值的不同。functionTPrototype_Font2.Clone:TObject;varTemp:TPrototype_Font2;beginTemp:=TPrototype_Font2.Create;Temp.Name:=self.N
26、ame;Temp.Size:=self.Size;Temp.Color:=self.Color;result:=Temp;end;但是,有時(shí)候某些屬性值并不是客戶(hù)所需要的,所以在克隆時(shí)對(duì)對(duì)象的屬性值進(jìn)行取舍是允許的。TPrototype_Font2中的Clone函數(shù)中,我只復(fù)制了影響字體外觀的3個(gè)最主要的屬性。這一取舍的后果是,克隆的新字體并不完全和原型字體一致,比如,不能反映出是否是斜體。這種克隆稱(chēng)為不完全克隆。示例程序94原型字體程序的實(shí)現(xiàn)源碼unitPrototypeFont;interfaceusesSysUtils,Windows,Messages,Classes,Graphics,
27、Controls,Forms,Dialogs;typeTPrototype_Font=class(TFont)publicfunctionClone:TObject;virtual;abstract;procedureSetFont;virtual;abstract;end;TPrototype_Font1=class(TPrototype_Font)publicfunctionClone:TObject;override;procedureSetFont;override;end;TPrototype_Font2=class(TPrototype_Font)publicfunctionClo
28、ne:TObject;override;procedureSetFont;override;end;implementationTPrototype_Font1調(diào)用Assign方法實(shí)現(xiàn)的字體克隆。functionTPrototype_Font1.Clone:TObject;varTemp:TPrototype_Font1;beginTemp:=TPrototype_Font1.Create;Temp.Assign(self);result:=Temp;end;procedureTPrototype_Font1.SetFont;varFontDialog:TFontDialog;beginFon
29、tDialog:=TFontDialog.Create(nil);tryifFontDialog.ExecutethenTFont(self).Assign(FontDialog.Font);finallyFontDialog.Free;end;end;TPrototype_Font2通過(guò)自己編程實(shí)現(xiàn)的字體克隆。functionTPrototype_Font2.Clone:TObject;varTemp:TPrototype_Font2;beginTemp:=TPrototype_Font2.Create;Temp.Name:=self.Name;Temp.Size:=self.Size;Te
30、mp.Color:=self.Color;result:=Temp;end;procedureTPrototype_Font2.SetFont;varFontDialog:TFontDialog;beginFontDialog:=TFontDialog.Create(nil);tryifFontDialog.Executethenbeginself.Name:=FontDialog.Font.Name;self.Size:=FontDialog.Font.Size;self.Color:=FontDialog.Font.Color;end;finallyFontDialog.Free;end;
31、end;end.在示例程序94所示的客戶(hù)程序中,我設(shè)計(jì)了一個(gè)演示界面。通過(guò)“設(shè)置字體”按鈕可以設(shè)置原型字體,然后點(diǎn)擊“-克隆”按鈕,可以將原型字體克隆到左邊的文本編輯框中。如圖所示。注意這里使用的多態(tài)和轉(zhuǎn)型技術(shù)。TPrototype_Font的clone函數(shù)把克隆的對(duì)象向下轉(zhuǎn)型為T(mén)Object傳出,示例程序94中再把返回的對(duì)象向上轉(zhuǎn)型為T(mén)Prototype_Font。因?yàn)門(mén)Font是TPrototype_Font的基類(lèi),下面的寫(xiě)法是安全的。Memo1.Font:=TPrototype_Font(FPrototype_Font1.clone);示例程序95客戶(hù)程序源碼unitMainForm;i
32、nterfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls,PrototypeFont,ExtCtrls;typeTForm1=class(TForm)Memo1:TMemo;btnSet1:TButton;btnClone1:TButton;btnClone2:TButton;Memo2:TMemo;btnSet2:TButton;Bevel1:TBevel;procedureFormCreate(Sender:TObject);procedurebtnClon
33、e1Click(Sender:TObject);procedurebtnSet1Click(Sender:TObject);procedurebtnClone2Click(Sender:TObject);procedurebtnSet2Click(Sender:TObject);privateFPrototype_Font1:TPrototype_Font;FPrototype_Font2:TPrototype_Font;end;varForm1:TForm1;implementation$R*.dfmprocedureTForm1.FormCreate(Sender:TObject);beg
34、inFPrototype_Font1:=TPrototype_Font1.Create;FPrototype_Font2:=TPrototype_Font2.Create;Memo1.Lines.Add(這里演示調(diào)用Assign方法實(shí)現(xiàn)的字體克隆。);Memo2.Lines.Add(這里演示通過(guò)自己編程實(shí)現(xiàn)的字體克隆。);end;procedureTForm1.btnClone1Click(Sender:TObject);beginMemo1.Font:=TPrototype_Font(FPrototype_Font1.clone);end;procedureTForm1.btnSet1Cli
35、ck(Sender:TObject);beginFPrototype_Font1.SetFont;end;procedureTForm1.btnClone2Click(Sender:TObject);varPrototype_Font2:TPrototype_Font2;beginMemo2.Font:=TPrototype_Font(FPrototype_Font2.clone);end;procedureTForm1.btnSet2Click(Sender:TObject);beginFPrototype_Font2.SetFont;end;end.5、使用Delphi流技術(shù)克隆對(duì)象實(shí)現(xiàn)原
36、型模式在Delphi中,流對(duì)象與流化存儲(chǔ)技術(shù)不僅是Delphi可視化設(shè)計(jì)實(shí)現(xiàn)的核心,它也為實(shí)現(xiàn)原型模式提供了更為方便、簡(jiǎn)單和通用的對(duì)象克隆方法。盡管流化存儲(chǔ)所涉及的存儲(chǔ)媒介十分廣泛,但在各對(duì)象的接口上得到了統(tǒng)一,使程序的存儲(chǔ)操作變得十分方便、簡(jiǎn)單,從而使程序員能站在更高層面上進(jìn)行對(duì)象存取的有關(guān)編程工作而無(wú)需考慮存儲(chǔ)介質(zhì)的具體差異。在VCL中。從TPersistent類(lèi)開(kāi)始提供了一個(gè)接口,引入了對(duì)象的可賦值性和流化(assignmentandstreamingcapabilities)等性質(zhì)TPersistent類(lèi)是抽象類(lèi),沒(méi)有實(shí)例對(duì)象。但該類(lèi)實(shí)現(xiàn)了對(duì)象公布(Published)屬性的存取,即在
37、該類(lèi)及其派生類(lèi)中聲明為Published的屬性、方法和事件等可在設(shè)計(jì)期時(shí)顯示在ObjectInspector窗中,能在ObjectInspector中對(duì)對(duì)象的Published屬性進(jìn)行設(shè)計(jì)期的設(shè)計(jì),并可將設(shè)置的值存到窗體或數(shù)據(jù)模塊的DFM文件中。在程序運(yùn)行期,對(duì)象將被初始化為設(shè)計(jì)期所設(shè)置的狀態(tài)。例如,在Delphi窗體上設(shè)計(jì)的按鈕對(duì)象,在程序運(yùn)行期該對(duì)象將被初始化為設(shè)計(jì)期所設(shè)置的狀態(tài)。在編程中為了實(shí)現(xiàn)流化,我們需要使用TStream的派生類(lèi)。TStream是所有流類(lèi)的抽象基類(lèi),它繼承自TObject。它的派生類(lèi)主要用于對(duì)文本、內(nèi)存、數(shù)據(jù)庫(kù)的Blob字段、數(shù)據(jù)壓縮等進(jìn)行操作。由于TStream與
38、具體的存儲(chǔ)無(wú)關(guān),派生類(lèi)卻與存儲(chǔ)媒介緊密相關(guān),因此每個(gè)子類(lèi)都必須實(shí)現(xiàn)與具體存儲(chǔ)媒介相關(guān)的方法,如磁盤(pán)、內(nèi)存等。通過(guò)流的寫(xiě)方法我們可以把對(duì)象轉(zhuǎn)化成位模式保存在內(nèi)存或文件中;同樣,通過(guò)流的讀方法我們可以把保存在內(nèi)存或文件中的位模式重新轉(zhuǎn)化成對(duì)象。通常把對(duì)象寫(xiě)到流里的過(guò)程稱(chēng)為串行化,而把對(duì)象從流中讀出來(lái)的過(guò)程稱(chēng)為并行化。通過(guò)對(duì)象的一進(jìn)一出,實(shí)際上就實(shí)現(xiàn)了對(duì)象的克隆。需要注意的是,能夠被流化的對(duì)象必須是TPersistent的派生類(lèi)。一般Delphi的組件(TComponent的派生類(lèi))都是支持流化的。用戶(hù)要使自己定義的類(lèi)能夠支持流化,通常至少應(yīng)該使其繼承自TPersistent。參閱:需要進(jìn)一步了解D
39、elphi對(duì)象流化機(jī)制以及VCL相關(guān)的類(lèi),請(qǐng)參閱我著的Delphi面向?qū)ο缶幊趟枷?機(jī)械工業(yè)出版社2003年9月)。下面我通過(guò)一個(gè)例子來(lái)演示說(shuō)明如何使用流克隆對(duì)象實(shí)現(xiàn)原型模式。這個(gè)例子有點(diǎn)類(lèi)似前圖所示的通過(guò)Delphi的IDE復(fù)制和粘貼按鈕對(duì)象。下圖是演示程序的運(yùn)行界面。我們點(diǎn)擊“克隆對(duì)象”按鈕,就會(huì)把一個(gè)文本框克隆到窗體上。為此,我使用原型模式設(shè)計(jì)了下圖的類(lèi)結(jié)構(gòu)。與前面原型模式示例程序不同的是,這里的TMemoPrototype類(lèi)Clone方法利用了對(duì)象流化來(lái)完成對(duì)象的克隆。functionTMemoPrototype.Clone:TObject;vartmp:TMemoPrototype;
40、TmpStream:TStream;beginWriteComponentResFile(MemoPrototype.dat,self);tmp:=TMemoPrototype(ReadComponentResFile(MemoPrototype.dat,nil);result:=tmp;end;這里我們使用了Delphi的Classes單元中兩個(gè)全局函數(shù)WriteComponentResFile和ReadComponentResFile來(lái)完成對(duì)象的串行化和并行化過(guò)程,前者將對(duì)象以位模式寫(xiě)到資源文件MemoPrototype.dat中,后者從資源文件MemoPrototype.dat中讀出對(duì)象
41、,每讀出一個(gè)對(duì)象就好像克隆出了一個(gè)對(duì)象。這兩個(gè)函數(shù)封裝了文件流(TFileStream)的操作過(guò)程,使我們編程簡(jiǎn)單化,不過(guò)從他們的實(shí)現(xiàn)代碼中(示例程序96),我們還是可以看出這一流化過(guò)程的。示例程序96WriteComponentResFile和ReadComponentResFile的實(shí)現(xiàn)代碼procedureWriteComponentResFile(constFileName:string;Instance:TComponent);varStream:TStream;beginStream:=TFileStream.Create(FileName,fmCreate);tryStream.
42、WriteComponentRes(Instance.ClassName,Instance);finallyStream.Free;end;end;functionReadComponentResFile(constFileName:string;Instance:TComponent):TComponent;varStream:TStream;beginStream:=TFileStream.Create(FileName,fmOpenReadorfmShareDenyWrite);tryResult:=Stream.ReadComponentRes(Instance);finallyStr
43、eam.Free;end;end;于是,在使用克隆對(duì)象的演示窗體中,我們通過(guò)點(diǎn)擊“克隆對(duì)象”按鈕,觸發(fā)了以下按鈕事件:procedureTForm1.btnCloneClick(Sender:TObject);vartmp:TMemoPrototype;begintmp:=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)
44、;end;通過(guò)克隆原型對(duì)象的方法,我們將方便地得到TMemoPrototype的實(shí)例。這些對(duì)象有著和原型對(duì)象一樣的狀態(tài),比如:相同的外觀和字體。(為了便于演示,我有意調(diào)整了他們的位置,否則窗體上的文本框會(huì)重疊在一起。)由此可見(jiàn),流化對(duì)象的強(qiáng)大之處在于無(wú)需復(fù)雜的處理就可以將對(duì)象和一個(gè)二進(jìn)制流之間進(jìn)行互相轉(zhuǎn)化。這一功能可以巧妙地被我們用于對(duì)象克隆,特別是當(dāng)某些對(duì)象不提供Assign方法實(shí)現(xiàn)時(shí)。6、Delphi對(duì)象的深克隆前面我講過(guò),當(dāng)克隆一個(gè)對(duì)象時(shí),如果克隆的是該對(duì)象所有的狀態(tài),即被克隆對(duì)象的所有變量都含有與原始對(duì)象相同的值,稱(chēng)為完全TStreamableClass(ReadComponentRe
45、sFile(DeepClone,nil);TStreamableClass(ReadComponentResFile(DeepClone,nil);克隆。否則,有選擇地克隆原始對(duì)象的部分狀態(tài),只能稱(chēng)為不完全克隆。這是從對(duì)象克隆的廣度上看問(wèn)題。如果從對(duì)象克隆的深度上看,對(duì)象克隆還可以分成淺克隆和深克隆。所謂淺克隆僅僅克隆所考慮的對(duì)象,而不深入克隆它所引用的對(duì)象。如果在克隆當(dāng)前對(duì)象時(shí),同時(shí)也克隆了該對(duì)象所引用的對(duì)象,這就是所謂的深克隆了。在淺克隆中,被克隆的對(duì)象所引用的對(duì)象仍然是原始對(duì)象所引用的那個(gè)對(duì)象;而深克隆中,被克隆的對(duì)象所引用的對(duì)象已經(jīng)是一個(gè)新的對(duì)象。這就是說(shuō),深克隆同時(shí)也克隆了引用對(duì)象本
46、身,而不是它的一個(gè)引用。由于深克隆間接復(fù)制了引用的對(duì)象,如果出現(xiàn)循壞引用,可能會(huì)出現(xiàn)意想不到的問(wèn)題。所以當(dāng)一個(gè)類(lèi)引用了不支持串行化的間接對(duì)象,或者引用含有循環(huán)結(jié)構(gòu)的時(shí)候,則不能考慮使用深克隆。淺克隆和深克隆問(wèn)題也稱(chēng)為淺復(fù)制和深復(fù)制(shallowcopyversusdeepcopy)的問(wèn)題,該問(wèn)題的焦點(diǎn)在于是否復(fù)制實(shí)例變量,亦或只是共享該變量的引用。淺克隆是簡(jiǎn)單也容易達(dá)成的,但是復(fù)雜結(jié)構(gòu)原型的克隆一般需要進(jìn)行深復(fù)制,因?yàn)檫@樣可以保證原型對(duì)象與被克隆的對(duì)象彼此獨(dú)立,互不影響。雖然深克隆的問(wèn)題比較復(fù)雜,但使用流化對(duì)象的技巧可以方便實(shí)現(xiàn)對(duì)象的深克隆。下面我給出一個(gè)演示性的例子。下圖是這個(gè)原型模式深克
47、隆演示程序的類(lèi)圖。從圖中看出,TStreamableClass包含了FContainedClass和FMemo兩個(gè)數(shù)據(jù)成員變量,分別引用了TContainedClass和TMemoPrototype的實(shí)例對(duì)象。TStreamableClass的Clone方法實(shí)現(xiàn)了對(duì)象的深克隆。示例程序97是PrototypeByStream單元程序的源碼,這里實(shí)現(xiàn)了原型模式。需要注意的是,聲明對(duì)象時(shí),只有放置在published域的數(shù)據(jù)成員(或?qū)傩裕┎拍軌虮蛔詣?dòng)流化。也就是說(shuō),要克隆的引用對(duì)象變量要么直接放在published域(如TStreamableClass的數(shù)據(jù)成員FMemo),要么將訪問(wèn)其的屬性放在
48、published域(如TStreamableClass的屬性ContainedClass)。從流中讀出對(duì)象時(shí),需要將該對(duì)象轉(zhuǎn)型為原來(lái)的類(lèi)型,比如:AClassInstance:=TStreamableClass=class(TComponent)TStreamableClass=class(TComponent)但是事先要使用RegisterClass或RegisterClasses注冊(cè)自己的類(lèi),比如:RegisterClasses(TMemoPrototype,TContainedClass,TStreamableClass);否則會(huì)找不到類(lèi),出現(xiàn)EClassNotFound異常。示例程序
49、97PrototypeByStream單元程序源碼unitPrototypeByStream;interfaceusesSysUtils,Windows,Messages,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls;typeTMemoPrototype=class(TMemo)publicconstructorcreate(AOwner:TComponent);override;publishedfunctionClone:TObject;end;TContainedClass=class(TPersistent)privateFSomeD
50、ata:Integer;procedureSetSomeData(Value:Integer);publicconstructorCreate;publishedpropertySomeData:IntegerreadFSomeDatawriteSetSomeData;end;privatefunctionTStreamableClass.Clone:TObject;FContainedClass:TContainedClass;publicconstructorCreate(AOwner:TComponent);override;destructorDestroy;override;publ
51、ishedFMemo:TMemoPrototype;functionClone:TObject;propertyContainedClass:TContainedClassreadFContainedClasswriteFContainedClass;end;implementationprocedureTContainedClass.SetSomeData(Value:Integer);beginFSomeData:=Value;end;constructorTContainedClass.Create;beginFSomeData:=42;end;constructorTStreamabl
52、eClass.Create(AOwner:TComponent);begininheritedCreate(AOwner);FContainedClass:=TContainedClass.Create;FMemo:=TMemoPrototype.create(AOwner);end;destructorTStreamableClass.Destroy;beginFContainedClass.Free;end;varAClassInstance:TStreamableClass;beginAClassInstance:=TStreamableClass.Create(nil);WriteCo
53、mponentResFile(DeepClone,AClassInstance);FreeAndNil(AClassInstance);AClassInstance:=TStreamableClass(ReadComponentResFile(DeepClone,nil);result:=AClassInstance;end;TMemoPrototypefunctionTMemoPrototype.Clone:TObject;vartmp:TMemoPrototype;TmpStream:TStream;beginWriteComponentResFile(MemoPrototype.dat,self);tmp:=TMemoPrototype(ReadComponentResFile(MemoPrototype.dat,nil);result:=tmp;end;constructorTMemoPrototype.create(AOwner:TComponent);begininherited;Width:=100;Height:=50;Left:=50;To
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年KTV特色主題活動(dòng)策劃與執(zhí)行合同3篇
- 2025版攤鋪機(jī)租賃及施工質(zhì)量保障合同范本6篇
- 個(gè)人健身教練合同:2024版專(zhuān)業(yè)輔導(dǎo)合同書(shū)
- 2025年度臨時(shí)用工勞務(wù)合同編制指南范本2篇
- 二零二五年度光伏電站運(yùn)維人工勞務(wù)合同范本3篇
- 2025年木材市場(chǎng)分析與預(yù)測(cè)合作合同范本
- 二零二五版木門(mén)行業(yè)展會(huì)參展與推廣服務(wù)合同4篇
- 二零二五年度數(shù)字貨幣技術(shù)研發(fā)與應(yīng)用合同集2篇
- 2025年戶(hù)外健身路徑欄桿設(shè)施采購(gòu)合同3篇
- 2025年度獵頭服務(wù)人才引進(jìn)與培養(yǎng)合作協(xié)議5篇
- 《電影之創(chuàng)戰(zhàn)紀(jì)》課件
- 社區(qū)醫(yī)療抗菌藥物分級(jí)管理方案
- 開(kāi)題報(bào)告-鑄牢中華民族共同體意識(shí)的學(xué)校教育研究
- 《醫(yī)院標(biāo)識(shí)牌規(guī)劃設(shè)計(jì)方案》
- 公司2025年會(huì)暨員工團(tuán)隊(duì)頒獎(jiǎng)盛典攜手同行共創(chuàng)未來(lái)模板
- 夜市運(yùn)營(yíng)投標(biāo)方案(技術(shù)方案)
- 電接點(diǎn) 水位計(jì)工作原理及故障處理
- 國(guó)家職業(yè)大典
- 2024版房產(chǎn)代持協(xié)議書(shū)樣本
- 公眾號(hào)運(yùn)營(yíng)實(shí)戰(zhàn)手冊(cè)
- 科研倫理與學(xué)術(shù)規(guī)范(研究生)期末試題庫(kù)及答案
評(píng)論
0/150
提交評(píng)論