程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> Delphi VCL Framework中的Action模式

Delphi VCL Framework中的Action模式

編輯:.NET實例教程
學習過設計模式的人都知道有一種行為模式叫做Command模式。在Delphi的VCL Framework中也使用到了這種模式,那就是Action模式。

    命令模式使用的目的在於使用對象來封裝客戶端的請求命令,由於使用以對象封裝,因此可以達到下面的效果:

  • 請求對象可結合多態以及虛擬方法來提供更大的彈性;
  • 負責執行請求的目的對象可以和客戶端分離,這就表示多個客戶端可以發生相同的請求對象,例如菜單或是工具欄按鈕都可以發生打開文件的請求,如此一來菜單和工具欄按鈕便可以使用相同的請求對象,而負責打開文件的程序代碼並不會綁定到單一的菜單項或是工具欄按鈕;
  • 由於使用了請求對象,因此不單是圖形用戶界面可以觸發請求,一般的程序代碼也可以通過請求對象來執行特定的工作;
  • 由於請求對象可以使用一個完整的類架構來實現,因此可以讓客戶端使用一致的程序代碼格式來觸發各種不同的請求。 

實現

    在Delphi的Classes單元中提供了Action設計模式的實現類和程序代碼。



  TBasicAction = class(TComponent)
  private
    FActionComponent: TComponent;
    FOnChange: TNotifyEvent;
    FOnExecute: TNotifyEvent;
    FOnUpdate: TNotifyEvent;
    procedure SetActionComponent(const Value: TComponent);
  protected
    FClIEnts: TList;
    procedure Change; virtual;
    procedure SetOnExecute(Value: TNotifyEvent); virtual;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function HandlesTarget(Target: TObject): Boolean; virtual;
    procedure UpdateTarget(Target: TObject); virtual;
    procedure ExecuteTarget(Target: TObject); virtual;
    function Execute: Boolean; dynamic;
    procedure RegisterChanges(Value: TBasicActionLink);
    procedure UnRegisterChanges(Value: TBasicActionLink);
    function Update: Boolean; virtual;
    property ActionComponent: TComponent read FActionComponent write SetActionComponent;
    property OnExecute: TNotifyEvent read FOnExecute write SetOnExecute;
    property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
  end;

...{ TBasicAction }

constructor TBasicAction.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FClIEnts := TList.Create;
end;

destructor TBasicAction.Destroy;
begin
  inherited Destroy;
  if Assigned(ActionComponent) then
    ActionComponent.RemoveFreeNotification(Self);
  while FClIEnts.Count > 0 do
    UnRegisterChanges(TBasicActionLink(FClIEnts.Last));
  FreeAndNil(FClIEnts);
end;

function TBasicAction.HandlesTarget(Target: TObject): Boolean;
begin
  Result := False;
end;

procedure TBasicAction.ExecuteTarget(Target: TObject);
begin
end;

procedure TBasicAction.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (AComponent = ActionComponent) then
    FActionComponent := nil;
end;

procedure TBasicAction.UpdateTarget(Target: TObject);
begin
end;

function TBasicAction.Execute: Boolean;
begin
  if Assigned(FOnExecute) then
begin
    FOnExecute(Self);
    Result := True;
  end
  else Result := False;
end;

function TBasicAction.Update: Boolean;
begin
  if Assigned(FOnUpdate) then
  begin
    FOnUpdate(Self);
    Result := True;
  end
  else Result := False;
end;

procedure TBasicAction.SetOnExecute(Value: TNotifyEvent);
var
  I: Integer;
begin
  if (TMethod(Value).Code <> TMethod(OnExecute).Code) or
     (TMethod(Value).Data <> TMethod(OnExecute).Data) then
  begin
    for I := 0 to FClIEnts.Count - 1 do
      TBasicActionLink(FClIEnts[I]).SetOnExecute(Value);
    FOnExecute := Value;
    Change;
  end;
end;

procedure TBasicAction.Change;
begin
  if Assigned(FOnChange) then FOnChange(Self);
end;

procedure TBasicAction.RegisterChanges(Value: TBasicActionLink);
begin
  Value.FAction := Self;
  FClIEnts.Add(Value);
end;

procedure TBasicAction.UnRegisterChanges(Value: TBasicActionLink);
var
 &nbsp;I: Integer;
begin
  for I := 0 to FClIEnts.Count - 1 do
    if FClIEnts[I] = Value then
    begin
      Value.FAction := nil;
      FClIEnts.Delete(I);
      Break;
    end;
end;

procedure TBasicAction.SetActionComponent(const Value: TComponent);
begin
  if FActionComponent <> Value then
  begin
    if Assigned(FActionComponent) then
      FActionComponent.RemoveFreeNotification(Self);
    FActionComponent := Value;
    if Assigned(FActionComponent) then
      FActionComponent.FreeNotification(Self);
  end;
end;TBasicAction類中聲明了三個關鍵的虛護方法以及一個關鍵的動態方法。

    function HandlesTarget(Target: TObject): Boolean; virtual;
    procedure UpdateTarget(Target: TObject); virtual;
    procedure ExecuteTarget(Target: TObject); virtual;
    function Execute: Boolean; dynamic;其中的動態方法Execute可以由TBasicActionLink類或是TBasicActionLink的派生類或是客戶端程序代碼調用,而該方法則會執行程序員在它的OnExecute事件中編寫的事件處理程序。對於TBasicAction的派生類而言,例如處理Paste動作的TEditPase類,就可以改寫HandlerTarget虛方法,並且在其中編寫執行粘貼的程序代碼。

  TEditPaste = class(TEditAction)
  public
    procedure UpdateTarget(Target: TObject); override;
    procedure ExecuteTarget(Target: TObject); override;
  end;

{ TEditPaste }
procedure TEditPaste.ExecuteTarget(Target: TObject);
begin
  GetControl(Target).PasteFromClipboard;
end;
procedure TEditPaste.UpdateTarget(Target: TObject);
begin
  Enabled := Clipboard.HasFormat(CF_TEXT);
end;

    因此,當我們要使用Action設計模式時,可以編寫TBasicAction的派生類,並且改寫ExecuteTarget虛方法,就像上面提到的TEditPaste類一樣。或是實現企業邏輯程序代碼並且把它指定給TBasicAction類的OnExecute事件,然後再調用Execute虛方法。

    我們通過繼承TBasicAction類實現了請求對象類,那麼如何將客戶端與這些請求對象建立關聯呢?這裡就用到了TBasicActionLink。



  TBasicActionLink = class(TObject)
  private
    FOnChange: TNotifyEvent;
 protected
    FAction: TBasicAction;
    procedure AssignClient(AClIEnt: TObject); virtual;
    procedure Change; virtual;
    function IsOnExecuteLinked: Boolean; virtual;
    procedure SetAction(Value: TBasicAction); virtual;
    procedure SetOnExecute(Value: TNotifyEvent); virtual;
  public
    constructor Create(AClIEnt: TObject); virtual;
    destructor Destroy; override;
    function Execute(AComponent: TComponent = nil): Boolean; virtual;
    function Update: Boolean; virtual;
    property Action: TBasicAction read FAction write SetAction;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

...{ TBasicActionLink }

constructor TBasicActionLink.Create(AClIEnt: TObject);
begin
  inherited Create;
  AssignClient(AClIEnt);
end;

procedure TBasicActionLink.AssignClient(AClIEnt: TObject);
begin
end;

destructor TBasicActionLink.Destroy;
begin
  if FAction <> nil then FAction.UnRegisterChanges(Self);
  inherited Destroy;
end;

procedure TBasicActionLink.Change;
begin
&nbsp; if Assigned(OnChange) then OnChange(FAction);
end;

function TBasicActionLink.Execute(AComponent: TComponent): Boolean;
begin
  FAction.ActionComponent := AComponent;
  Result := FAction.Execute;
end;

procedure TBasicActionLink.SetAction(Value: TBasicAction);
begin
  if Value <> FAction then
  begin
    if FAction <> nil then FAction.UnRegisterChanges(Self);
    FAction := Value;
    if Value <> nil then Value.RegisterChanges(Self);
  end;
end;

function TBasicActionLink.IsOnExecuteLinked: Boolean;
begin
  Result := True;
end;

procedure TBasicActionLink.SetOnExecute(Value: TNotifyEvent);
begin
end;

function TBasicActionLink.Update: Boolean;
begin
  Result := FAction.Update;
end;

    在上面的代碼中我們可以看到TBasicActionLink的Execute方法實際上也就是調用了TBasicAction對象的虛方法Execute來負責響應客戶端的請求。

應用舉例

    在通常的UI設計中,我們會在Form上放置一些菜單項,同時會把部分使用頻率較高的功能以工具欄形式提供給用戶。這些工具欄按鈕實現的功能與菜單項完全相同,我們就可以使用Action模式來設計這些請求,然後將菜單項和工具欄按鈕與這些Action對象對立關聯即可。即使以後在用戶界面上增加其它形式的調用,如上下文菜單,或是快捷鍵等,都可以直接與這些請求對象建立關聯。可以很輕松地擴充用戶發出請求的方式。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved