在Delphi中進行UI設計時,我們會在Form上放置許多的組件,當我們更改窗體的某些屬性時,其上所有組件的相應屬性都會隨著發生改變,這是如何實現的呢?這裡就使用到了Delphi VCL Framework中使用最多的一種設計模式 -- Notify模式。
Notify模式最經常發生的應用就是在容器類中的應用。由於容器類管理了許多子類對象,因此客戶程序代碼可能希望一次操作對所有容器類管理的子類對象進行特定的工作。在這種需求應用中就可以使用Notify模式,客戶程序代碼只需要傳送事件給容器類,而容器類在接收到這個事件之後再逐一的通知它所管理的子類對象。
Delphi中所有組件都是從TComponent類繼承而來的,只需要在這個類中實現通知模式,那麼它所有的子類對象便都具備了這種模式能力。我們剖析一下Delphi是如何實現這個模式的。
首先打開TComponent所在的單元文件Classes.pas,查找到TComponent類的定義,我們可以看到如下代碼(注意加粗的部分)
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
private
.......
FFreeNotifIEs: TList;
.......
procedure RemoveNotification(AComponent: TComponent);
.......
protected
.......
procedure Notification(AComponent: TComponent;
Operation: TOperation); virtual;
.......
public
constructor Create(AOwner: TComponent); virtual;
destructor Destroy; override;
.......
procedure FreeNotification(AComponent: TComponent);
procedure RemoveFreeNotification(AComponent: TComponent);
.......
published
property Name: TComponentName read FName write SetName stored False;
property Tag: Longint read FTag write FTag default 0;
end;
當一個組件被創建時,會通過FreeNotification方法將其添加到父組件的FFreeNotifIEs列表中,確保父組件發生變化時自己能夠被通知到;同時父組件也被添加到自己的FFreeNotifIEs列表中,確保自己發生變化時能夠通知到父組件。
procedure TComponent.FreeNotification(AComponent: TComponent);
begin
if (Owner = nil) or (AComponent.Owner <> Owner) then
begin
// Never acquire a reference to a component that is being deleted.
assert(not (csDestroying in (ComponentState + AComponent.ComponentState)));
if not Assigned(FFreeNotifies) then FFreeNotifIEs := TList.Create;
if FFreeNotifIEs.IndexOf(AComponent) < 0 then
begin
FFreeNotifIEs.Add(AComponent);
AComponent.FreeNotification(Self);
end;
end;
Include(FComponentState, csFreeNotification);
end;
與FreeNotification方法對應的有一個RemoveFreeNotification方法,此方法是將已經建立關聯關系的組件從彼此的FFreeNotifIEs列表中刪除,取消了由FreeNofifIEs建立的關聯。
procedure TComponent.RemoveFreeNotification(AComponent: TComponent);
begin
RemoveNotification(AComponent);
AComponent.RemoveNotification(Self);
end;
當一個組件發生變化時,則會調用Notification方法通知FreeNofifIEs列表中所有的組件。
procedure TComponent.Notification(AComponent: TComponent;
Operation: TOperation);
var
I: Integer;
begin
if (Operation = opRemove) and (AComponent <> nil) then
RemoveFreeNotification(AComponent);
if FComponents <> nil then
begin
I := FComponents.Count - 1;
while I >= 0 do
begin
TComponent(FComponents[I]).Notification(AComponent, Operation);
Dec(I);
if I >= FComponents.Count then
I := FComponents.Count - 1;
end;
end;
end;
與Notification方法對應的亦有一個私有的RemoveNotification方法,此方法的作用是將與之關聯的組件從FFreeNotifIEs列表中去除,如果沒有與之關聯的組件,則銷毀FFreeNotifIEs列表。
procedure TComponent.RemoveNotification(AComponent: TComponent);
begin
if FFreeNotifIEs <> nil then
begin
FFreeNotifIEs.Remove(AComponent);
if FFreeNotifIEs.Count = 0 then
begin
FFreeNotifIEs.Free;
FFreeNotifIEs := nil;
end;
end;
end;
小結一下
從TComponent類的定義代碼可以看出Delphi只將FreeNotification和RemoveFreeNotification方法公開出來,是希望我們利用這兩個方法在建立和取消組件間的關聯;而將Notification方法定義為受保護的虛方法,則為我們提供了改寫通知組件時執行附加操作的機會。
使用時機
- 在設計容器類時程序員想定義和外界一致的交互接口
- 需要對一群組件執行預定義好的特定工作
應用舉例
在編寫MIS軟件時,會涉及到一些單據的填寫,就以進貨單為例。在一張進貨單中會填寫貨品的單價、價格,我們希望在這兩個字段的值發生變化時系統能夠自動的計算出最新的總價。在這裡,我們就可以借鑒Notify模式,將數量與單價加入到彼此的通知列表中,並編寫通知處理方法如上面講到的Notification方法。這樣,無論單價還是數量發生變化都會觸發通知事件,進而達到重新計算總價的目的。
個人拙見,還望各位斧正。