面向對象技術是 90 年代主流,以 OO 方法建構並滲透 OO 風格的軟體擁有更高品質。在 OO 程式設計中,物件導向資料管理佔重要地位。Delphi 作為結合視覺化設計與 OO 語言的整合開發環境,其核心是元件(一種物件),應用程式完全由元件建構,開發高效能 Delphi 應用必然涉及物件導向資料管理技術。
物件導向資料管理包含兩面向:
Delphi 兩方面表現優異。早期 Turbo Pascal 即有 Stream、Collection、Resource 等專用類別;Delphi 大幅增強,歸納為 Stream 物件(串流) 與 Filer 物件(讀寫器),廣泛應用於 VCL。它們不僅提供記憶體、外部儲存、Windows 資源中的物件管理,更支援資料庫 BLOB 欄位中的物件操作。
本章介紹 Stream 物件與 Filer 物件的實作原理、應用方法及在超媒體系統中的應用,對運用 Delphi 開發高階應用極為重要。
Stream 物件(串流物件)統稱 TStream、THandleStream、TFileStream、TMemoryStream、TResourceStream、TBlobStream 等。它們分別代表在各種媒介上儲存資料的能力,將各類資料型別(含物件與元件)在記憶體、外部儲存、資料庫欄位中的管理操作抽象為物件方法,充分發揮 OO 技術優勢,應用程式可輕易在各種 Stream 物件間複製資料。
TStream 是能在各種媒介中儲存二進位資料的物件的抽象基底類別。繼承自 TStream 的物件用於在記憶體、Windows 資源檔、磁碟檔案、資料庫欄位等媒介儲存資料。
TStream 定義兩個屬性:Size 與 Position,分別以位元組為單位表示串流大小與目前指標位置。定義的方法用於在各種串流中讀、寫、相互複製二進位資料。所有 Stream 物件皆繼承自 TStream,故其定義的欄位與方法均可被呼叫。得益於 OO 動態連結特性,TStream 為各類串流提供統一介面,簡化使用;不同 Stream 物件抽象不同儲存媒介操作,TStream 方法為跨媒介資料複製提供最簡潔手段。
property Position: Longint;
指明串流中讀寫的目前位移量。
property Size: Longint;
指明以位元組為單位的串流大小,唯讀。
function CopyFrom(Source: TStream; Count: Longint): Longint;
從 Source 複製 Count 位元組到目前串流,指標從目前位置移動 Count 位元組,回傳實際複製位元組數。
function Read(var Buffer; Count: Longint): Longint; virtual; abstract;
從目前串流的目前位置複製 Count 位元組到 Buffer,指標後移 Count 位元組,回傳實際讀取位元組數。若回傳值小於 Count 表示讀取到達串流尾部。每個後繼 Stream 物件必須依其特定儲存媒介覆寫此方法。所有其他讀取方法(ReadBuffer、ReadComponent 等)最終皆呼叫 Read,動態連結優勢於此體現:後繼類別僅需覆寫 Read,其他讀取操作無需重新定義,TStream 提供統一介面。
procedure ReadBuffer(var Buffer; Count: Longint);
從串流複製 Count 位元組到 Buffer,指標後移 Count。若超過尾部引發 EReadError 例外。
function ReadComponent(Instance: TComponent): TComponent;
從目前串流讀取 Instance 指定的元件,回傳所讀元件。建立 TReader 呼叫 ReadRootComponent。若 Instance=nil 依串流中類型資訊建立新元件並回傳。
function ReadComponentRes(Instance: TComponent): TComponent;
從串流讀取 Instance 指定元件,但串流目前位置必須是 WriteComponentRes 寫入元件的位置。先呼叫 ReadResHeader 再呼叫 ReadComponent。若無有效資源檔頭引發 EInvalidImage。Classes 單元亦有同名函數,基於應用程式資源建立串流執行相同操作。
procedure ReadResHeader;
從串流目前位置讀取 Windows 資源檔頭,指標移至檔頭末尾。若無有效資源檔頭引發 EInvalidImage。ReadComponentRes 會自動呼叫,通常程式設計師無需自行呼叫。
function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;
移動串流指標 Offset 位元組,起點由 Origin 指定。Offset 為負數時向串流頭部移動。Origin 取值對照表:
| 常數 | 值 | Seek 起點 | Offset 取值 |
|---|---|---|---|
| soFromBeginning | 0 | 串流開頭 | 正數 |
| soFromCurrent | 1 | 串流目前位置 | 正數或負數 |
| soFromEnd | 2 | 串流結尾 | 負數 |
function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
將 Buffer 中 Count 位元組寫入串流,指標向尾部移動 Count 位元組,回傳寫入位元組數。每個繼承 Stream 物件必須覆寫此方法提供特定儲存媒介寫入實作。所有其他寫入方法(WriteBuffer、WriteComponent 等)最終呼叫 Write 執行實際寫入。
procedure WriteBuffer(const Buffer; Count: Longint);
功能類似 Write,呼叫 Write 執行實際寫入,若未寫入所有位元組引發 EWriteError 例外。
procedure WriteComponent(Instance: TComponent);
將 Instance 指定元件及其擁有的所有元件寫入串流。建立 TWriter 呼叫 WriteRootComponent。
procedure WriteComponentRes(const ResName: String; Instance: TComponent);
先寫入標準 Windows 資源檔頭,再寫入 Instance 指定元件。讀取需呼叫 ReadComponentRes。以 ResName 作為資源名,再呼叫 WriteComponent。
procedure WriteDescendant(Instance, Ancestor: TComponent);
建立 TWriter 呼叫其 WriteDescendant 寫入 Instance。Instance 可為繼承自 Ancestor 元件的表單,或繼承表單中對應祖先表單中 Ancestor 元件的元件。
procedure WriteDescendantRes(const ResName: String; Instance, Ancestor: TComponent);
寫入 Windows 資源檔頭(以 ResName 為資源名),再呼叫 WriteDescendant 寫入 Instance。