《深入VCL核心架構剖析》讀書筆記


第一章 回到從前

VCL的設計目標是爲了幫助windows開發人員簡化開發流程,提供組件和麪向對象的方式進行軟件開發的工作。


第二章 Borland VCL Framework的誕生

由於Borland有了從OWL學習到的經驗和教訓,以及當時Microsoft VBX組件架構過於複雜和不易擴充的問題,因此Chuck在VCL Framework設計之初便設定了下面數個目標:

        使用單一繼承架構以避免陷入c++多重繼承的問題,同時這也有助於簡化Delphi編譯器的開發工作

VCL Framework 必須不限於16位或是32位平臺

VCL Framework 必須提供開發的組件架構,以允許程序員自定義組件

VCL Framework 必須進化成在可設計時期提供功能的 Framework

VCL Framework必須使用PME(Property-Event-Method)模型

VCL Framework必須使用面向對象技術來設計和實現

VCL Framework必須完善地封裝和分派窗口消息

    

第三章 面向對象程序語言和Framework

有了TComponent作爲組件的基礎父類之後,VCL控件類可以直接從TComponent類繼承下來嗎?這是一個非常關鍵的問題,因爲這牽涉到Framework架構設計的問題。讓我們討論一下,也讓我們先脫離Borland工程師的想法而思考一下。

1. 如果讓VCL控件類直接從TComponent繼承下來,那麼由於VCL Framework在設計之初就是封裝Windows API以及Windows的對象,因此如果我們聲明一個TWinComponent類從TComponent繼承下來:

TWinComponent = class( TComponent )
....
end;
接着再聲明各種VCL控件類從TWinComponet繼承下來:

TButton = class( TWinComponent )
...
end;

TListBox = class( TWinComponent )
...
end;
...

這樣的設計可以在TWinComponent類中聲明和實現所有VCL控件需要的基礎服務,至於每一個VCL控件特有的服務則是由每一個特定的VCL控件類來提供。如此一來TWinComponent可以設計成聲明許多的虛擬方法,並且在這些虛擬方法中提供基礎的實現程序代碼,再由VCL控件類來改寫這些基礎並且核心的虛擬方法。例如在Windows中控制窗口特性的WNDCLASSSA 數據類型是每一個Windows控制對象都必須填入的,並且每一個控件都需要填入不同的數值。因此TWinComponent可以定義類似如下的虛擬方法CreateParams,在TWinComponent中的CreateParams虛擬方法中可以在WNDCLASSA數據類型中填入大多數VCL控件共通的信息,而TWinComponent的派生類則可以改寫CreateParams再填入不同的信息。

TWinComponent = class( TComponent )
...
    procedure CreateParams( var Params: TCreateParams ); virtual;
end;

TButton = class( TWinComponent )
...
    procedure CreateParams( var Params:  TCreateParams ); override;
end;

TListBox = class( TWinComponent )
...
procedure CreateParams( var Params : TCreateParams ); overrride;
end;
...

依照這樣的設計思想可以讓TWinComponent和派生的VCL控件類正確地封裝Windows控件。但是這樣的設計架構讓VCL Framework和windows操作系統有着太緊密相依的關係,而且有着下面數項違反成爲通用Framework的缺點:

  • 沒有彈性的設計架構。如果採用核心通用類(TWinComponent)、直接繼承的實體Windows控件類(例如封裝Windows的Edit,ListBox等控件),那麼VCL Framework將很難再加入其它種類的控件類,因爲如此一來定義在TWinComponent中的數據結構或是虛擬方法直接受到Windows平臺和Windows控件的設計思想影響。
  • 無法提供開發人員簡易的實現自定義VCL組件的功能,因爲在TWinComponent和派生類直接沒有提供任何可讓開發人員繼承和實現自定義VCL類的機會。
  • 由於在Windows 3.x 和Windows95/98的時代Windows控件的Handle值都共享一塊極小的系統內存(Win95 64K, Win98 128K),因此係統可供使用Wndows控件有一定的限制。如果讓每一個實體Windows控件類直接從TWInComponent繼承下來,則TWinComponent一定得定義代表Windows控件的Handle值,那麼着將讓VCL Framework能夠提供VCL控件種類受到WIndows控件的限制,而且會浪費寶貴的系統Handle資源。這樣講讓VCL Framework無法提供不使用Windows Handle值的自定義VCL控件。
再讓我們討論第二種設計方式。
2.如果在TComponent類之下先設計一個控制類,這個控制類具備基本的控制服務,例如處理鼠標的服務,並且處理控制事件的服務以及處理光標服務等。讓這個控制類成爲其他具體控件類的父類,那麼封裝WIndows控件類或是其他VCL Framework自定義的控件類就可以從這個控制類繼承下來並且自動用於處理鼠標,光標和事件的基本功能,如此一來我們便可以讓TComponent和封裝Windows控件的派生類經由控制類而分離,解決了緊耦合的問題,也讓VCL Framework的空間類不成爲WIndows平臺專屬的類而提提功能過能夠封裝其他控件的可能性,這似乎是一個比較好設計。而控制類也成爲TComponent和封裝windows控件的派生類直接的Adapter。

根據上面的想法,我們可以設計一個控制類TControl從TComponent繼承下來,再讓其他封裝實體控制的類從TControl繼承下來,如下所示:
TControl = Class( TComponent )
//鼠標服務
//光標服務
//事件服務
...
end;

TWinControl = class( TControl )
...
end;

TVCLControl = class( TControl )
...
end;

TListBox = class( TWinControl )
...
end;

第四章 VCL Framework和窗口消息

VCL Framework提供的窗口消息封裝機制必須解決下面的問題,

   1. 如何把窗口消息正確分派到發生的窗口和控件中?

           2. 窗口消息如何分配給封裝控件的VCL封裝類?

  上面第一件工作如圖 4-1所示,在VCL Framework之中必須提供一種方法能夠根據窗口消息自動分配給不同的VCL封裝類。



圖 4-1 窗口消息如何分派給正確的窗口和控件
既然我們知道Windows操作系統是把窗口消息分派給窗口回調函數的,因此VCL Framework可以在VCL應用程序向根窗口註冊的回調回調函數中提供一種巧妙的方法來分派消息,這就是稍後討論的重點。
上面第二項問題的重心則是當VCL的分配消息決定了消息的目的控件之後,VCL到底如何分派消息?因爲在VCL Framework中有數十種不同的封裝類,如果在VCL封裝機制中需要一一地判斷目的控件種類再決定如何分派,那將是非常低效的解決方案。既然如何,有沒有什麼好方法讓VCL Framework的分配消息機制可以不管目地控件的種類而是用統一的程序代碼來分派消息呢?
採用虛擬方法或是接口似乎是可行的解決方式,就虛擬方法來說,VCL Framework的分派消息機制只要調用一個標準的虛擬方法,然後不同的VCL封裝類只需要重載此虛擬方法就可以被VCL的分派消息機制調用並且傳遞窗口消息作爲此虛擬方法的參數。如果以接口來說,可以讓VCL封裝類實現標準接口,那麼VCL的分派消息機制便可以先從目的組件中取得此標準接口,再調用此接口中的消息分派方法即可。不過在1993/1994年時Object Pascal 還沒有接口機制,因此VCL Framework選擇了使用虛擬方法來解決分配窗口消息的機制。



object pascal 

       關鍵字: type

功能:聲明類型,常用來聲明接口、類、結構、函數、自定義類型。


{ 1.聲明接口 }

type
 IMalloc = interface(IInterface) //說明見下(1.1)
 ['{00000002-0000-0000-C000-000000000046}'] //說明見下(1.2)
  function Alloc(Size: Integer): Pointer; stdcall;//說明見下(1.3)
  function Realloc(P: Pointer; Size: Integer): Pointer; stdcall;
  procedure Free(P: Pointer); stdcall;
  function GetSize(P: Pointer): Integer; stdcall;
  function DidAlloc(P: Pointer): Integer; stdcall;
  procedure HeapMinimize; stdcall;
 end;

{ 2.聲明類 }
type
  TMyObject1 = class(TObject)//說明見下(2.1)
  end;
type
  TMyObject2 = Object(父類)//說明見下(2.2)
  end;
{ 3.聲明類指針 }
Type 
    TStudent = Object(父類)   
    Private 
        FName:string; 
    Protected 
    Public 
      Procedure Find; 
end; 
    Pstudent = ^TStudent; //聲明類指針
var 
    P:PStudent; 
begin 
    New(P); 
    P^.FName:= '  '; 
....... 
........ 
    Dispose(p); 
end; 

{ 4.聲明結構 }
type
    TMyRecord = record 
    Name:String; 
    Sex:String[2]; 
    Age;Byte; 
end;

{ 5.聲明函數 }
type
  //函數聲明的一種方式,定義一個函數當作類型,允許這個函數被定義作爲參數用於子程序。
  TMyFunc = function(I: Integer): string;

{ 6.聲明自定義類型 }
type
  TCol = (cItemA, cItemB, cItemC);//枚舉類型
  TColSet = set of TCol;//定義有序數的子界。定義了一個整數或字符的範圍。詳見Set關鍵詞。
  TLatter = 'A' .. 'Z';//定義字符範圍
  TInt = Integer;//類似C語言的宏,用TInt代替Integer


第10章 VCL Framework的演化——VCL.net

        當VCL Framework在移植稱VCL.net時便面臨一個抉擇,那就是對於.NET Framework中提供類似VCL Framework的類,到底VCL.NET應該使用.NET Framework的類呢,還是繼續使用自己的類?最後VCL.NET覺得使用.NET Framework使用的類,並且搭配前面介紹的Helper Class以及Adapter設計模式來利用.NET Framework提供的類服務而又保持了和VCL Framework兼容的類接口。這是一個非常有技巧的設計,因爲VCL Framework中許多的類和Win32以及Windows 消息綁定得太緊密,因此如果VCL.NET能夠通過這次的.NET Framework以更兼容的讓是來移植,那麼將會是一個比較好的選擇,也可以節省VCL.NET重新再.NET 中實現這些緊密相依的類。

        因此在VCL.NET中我們可以看到TObject已經定義成.NET的System.Object,並且經由TObjectHelper來提供兼容性,TInterfacedObject類也重新定義成是TObject也就是System.Object。

        

type 
   TObject = System.Object;
   TCustomAttribute = System.Attribute
   TInterfacedObject = TObject;
        此外在VCL Framework中提供持久化重要功能的TPersistent 類也被定義成爲是.NET的System.MarshalByRefObject,因爲System.MArshalByRefObject提供了類似並且更爲完整的持久化功能。

TPersistent = System.MarshalByRefObject;

      爲了維持兼容性,因而VCL Framework中有大量的類使用了TPersistent,因此也定義了TPersistent的Helper Class。

TPersistentHelper = class helper( TObjectHelper ) for TPersistent

       此外如果讀者還記得前面討論持久化的章節,那麼就會記得TPersistent也是用了Adapter設計模式在和VCL Framework兼容的接口以.NET 的持久化功能來實現。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章