在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只将FreeNotificationRemoveFreeNotification方法公开出来,是希望我们利用这两个方法在建立和取消组件间的关联;而将Notification方法定义为受保护的虚方法,则为我们提供了改写通知组件时执行附加操作的机会。

使用时机

  •   在设计容器类时程序员想定义和外界一致的交互接口
  •   需要对一群组件执行预定义好的特定工作

应用举例

  在编写MIS软件时,会涉及到一些单据的填写,就以进货单为例。在一张进货单中会填写货品的单价、价格,我们希望在这两个字段的值发生变化时系统能够自动的计算出最新的总价。在这里,我们就可以借鉴Notify模式,将数量与单价加入到彼此的通知列表中,并编写通知处理方法如上面讲到的Notification方法。这样,无论单价还是数量发生变化都会触发通知事件,进而达到重新计算总价的目的。

  个人拙见,还望各位斧正。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐