面向對象技術是九十年代的主流技術,各類應用軟件如果以面向對象的方法構造並且滲透面向對象的風格將使軟件具有更高的品質。在面向對象程序設計中,對象式數據管理占有很重要的地位。在Delphi中,對對象式數據管理的支持方式是其一大特色。
Delphi是一個面向對象的可視化設計與面向對象的語言相結合的集成開發環境。Delphi的核心是部件。部件是對象的一種。Delphi應用程序完全是由部件來構造的,因此開發高性能的Delphi應用程序必然會涉及對象式數據管理技術。
對象式數據管理包括兩方面的內容:
● 用對象來管理數據
● 對各類數據對象(包括對象和部件)的管理
Delphi在這兩方面都做的相當出色。在Delphi的早期版本Turbo Pascal 中就曾有流(Stream)、群(Collection)和資源(Resource)等專門用於對象式數據管理的類。在Delphi中,這些功能得到了大大的加強。Delphi將對象式數據管理類歸結為Stream對象(Stream)和Filer對象(Filer),並將它們應用於可視部件類庫(VCL)的方方面面。它們不僅提供了在內存、外存和Windows資源中管理對象的功能,還提供了在數據庫BLOB字段中對象的功能。
在本章中將介紹Stream對象和Filer對象的實現原理、應用方法以及在超媒體系統中的應用。這對於運用Delphi 開發高級應用是很重要的。
20.1 流式對象的實現原理和應用
Stream對象,又稱流式對象,是TStream、THandleStream、TFileStream、TMemoryStream、TResourceStream和TBlobStream等的統稱。它們分別代表了在各種媒介上存儲數據的能力,它們將各種數據類型(包括對象和部件) 在內存、外存和數據庫字段中的管理操作抽象為對象方法,並且充分利用了面向對象技術的優點,應用程序可以相當容易地在各種Stream對象中拷貝數據。
下面介紹各種對象的數據和方法及使用方法。
20.1.1 TStream對象
TStream對象是能在各種媒介中存儲二進制數據的對象的抽象對象。從TStream 對象繼承的對象用於在內存、Windows資源文件、磁盤文件和數據庫字段等媒介中存儲數據。
TStream中定義了兩個屬性:Size和Position。它們分別以字節為單位表示的流的大小和當前指針位置。TStream中定義的方法用於在各種流中讀、寫和相互拷貝二進制數據。因為所有的Stream對象都是從TStream中繼承來的,所以在TStream中定義的域和方法都能被Stream對象調用和訪問。此外,又由於面向對象技術的動態聯編功能,TStream為各種流的應用提供了統一的接口,簡化了流的使用;不同Stream對象是抽象了對不同存儲媒介的數據上的操作,因此,TStream的需方法為在不同媒介間的數據拷貝提供了最簡捷的手段。
20.1.1.1 TStream的屬性和方法
1. Position屬性
聲明:property Position: Longint;
Position屬性指明流中讀寫的當前偏移量。
2. Size屬性
聲明:property Size: Longint;
Size屬性指明了以字節為單位的流的的大小,它是只讀的。
3. CopyFrom方法
聲明:function CopyFrom(Source: TStream; Count: Longint): Longint;
CopyFrom從Source所指定的流中拷貝Count個字節到當前流中, 並將指針從當前位置移動Count個字節數,函數返回值是實際拷貝的字節數。
4. Read方法
聲明:function Read(var Buffer; Count: Longint): Longint; virtual; abstract;
Read方法從當前流中的當前位置起將Count個字節的內容復制到Buffer中,並把當前指針向後移動Count個字節數,函數返回值是實際讀的字節數。如果返回值小於Count,這意味著讀操作在讀滿所需字節數前指針已經到達了流的尾部。
Read方法是抽象方法。每個後繼Stream對象都要根據自己特有的有關特定存儲媒介的讀操作覆蓋該方法。而且流的所有其它的讀數據的方法(如:ReadBuffer,ReadComponent等)在完成實際的讀操作時都調用了Read方法。面向對象的動態聯編的優點就體現在這兒。因為後繼Stream對象只需覆蓋Read方法,而其它讀操作(如ReadBuffer、ReadComponent等)都不需要重新定義,而且TStream還提供了統一的接口。
5. ReadBuffer方法
聲明:procedure ReadBuffer(var Buffer; Count: Longint);
ReadBuffer方法從流中將Count個字節復制到Buffer 中, 並將流的當前指針向後移動Count個字節。如讀操作超過流的尾部,ReadBuffer方法引起EReadError異常事件。
6. ReadComponent方法
聲明:function ReadComponent(Instance: TComponent): TComponent;
ReadComponent方法從當前流中讀取由Instance所指定的部件,函數返回所讀的部件。ReadComponent在讀Instance及其擁有的所有對象時創建了一個Reader對象並調用它的ReadRootComponent方法。
如果Instance為nil,ReadComponent的方法基於流中描述的部件類型信息創建部件,並返回新創建的部件。
7. ReadComponentRes方法
聲明:function ReadComponentRes(Instance: TComponent): TComponent;
ReadComponentRes方法從流中讀取Instance指定的部件,但是流的當前位置必須是由WriteComponentRes方法所寫入的部件的位置。
ReadComponentRes 首先調用ReadResHeader方法從流中讀取資源頭,然後調用ReadComponent方法讀取Instance。如果流的當前位置不包含一個資源頭。ReadResHeader將引發一個EInvalidImage異常事件。在Classes庫單元中也包含一個名為ReadComponentRes的函數,該函數執行相同的操作,只不過它基於應用程序包含的資源建立自己的流。
8. ReadResHeader方法
聲明:procedure ReadResHeader;
ReadResHeader方法從流的當前位置讀取Windows資源文件頭,並將流的當前位置指針移到該文件頭的尾部。如果流不包含一個有效的資源文件頭,ReadResHeader將引發一個EInvalidImage異常事件。
流的ReadComponentRes方法在從資源文件中讀取部件之前,會自動調用ReadResHeader方法,因此,通常程序員通常不需要自己調用它。
9. Seek方法
聲明:function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;
Seek方法將流的當前指針移動Offset個字節,字節移動的起點由Origin指定。如果Offset是負數,Seek方法將從所描述的起點往流的頭部移動。下表中列出了Origin的不同取值和它們的含義:
表20.1 函數Seek的參數的取值
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
常量 值 Seek的起點 Offset的取值
─────────────────────────────────
SoFromBeginning 0 流的開頭 正 數
SoFromCurrent 1 流的當前位置 正數或負數
SoFromEnd 2 流的結尾 負 數
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
10. Write方法
在Delphi對象式管理的對象中有兩類對象的方法都有稱為Write的:Stream對象和Filer對象。Stream對象的Write方法將數據寫進流中。Filer對象通過相關的流傳遞數據,在後文中會介紹這類方法。
Stream對象的Write方法聲明如下:
function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
Write方法將Buffer中的Count個字節寫入流中,並將當前位置指針向流的尾部移動Count個字節,函數返回寫入的字節數。
TStream的Write方法是抽象的,每個繼承的Stream對象都要通過覆蓋該方法來提供向特定存儲媒介(內存、磁盤文件等)寫數據的特定方法。流的其它所有寫數據的方法(如WriteBuffer、WriteComponent)都調用Write擔當實際的寫操作。
11. WriteBuffer方法
聲明:procedure WriteBuffer(const Buffer; Count: Longint);
WriteBuffer的功能與Write相似。WriteBuffer方法調用Write來執行實際的寫操作,如果流沒能寫所有字節,WriteBuffer會觸發一個EWriteError異常事件。
12. WriteComponent方法
在Stream對象和Filer對象都有被稱為WriteComponent的方法。Stream對象的WriteComponent方法將Instance所指定的部件和它所包含的所有部件都寫入流中;Writer對象的WriteComponent將指定部件的屬性值寫入Writer對象的流中。
Stream對象的WriteComponent方法聲明是這樣的:
procedure WriteComponent(Instance: Tcomponent);
WriteComponent創建一個Writer對象,並調用Writer的WriteRootComponent方法將Instance及其擁有的對象寫入流。
13. WriteComponentRes方法
聲明:WriteComponentRes(const ResName: String; Instance: TComponent);
WriteComponentRes方法首先往流中寫入標准Windows 資源文件頭,然後將Instance指定的部件寫入流中。要讀由WriteComponentRes寫入的部件,必須調用ReadComponentRes方法。
WriteComponentRes使用ResName傳入的字符串作為資源文件頭的資源名,然後調用WriteComponent方法將Instance和它擁有的部件寫入流。
14. WriteDescendant方法
聲明:procedure WriteDescendant(Instance Ancestor: TComponent);
Stream對象的WriteDescendant方法創建一個Writer對象,然後調入該對象的WriteDescendant方法將Instance部件寫入流中。Instance可以是從Ancestor部件繼承的窗體,也可以是在從祖先窗體中繼承的窗體中相應於祖先窗體中Ancestor部件的部件。
15. WriteDescendantRes方法
聲明:procedure WriteDescendantRes(const ResName: String;
Instance, Ancestor: TComponent);
WriteDescendantRes方法將Windows資源文件頭寫入流,並使用ResName作用資源名,然後調用WriteDescendant方法,將Instance寫入流。