现在的位置: 首页 > 综合 > 正文

VCL类学习之(一) TBasicAction

2017年12月02日 ⁄ 综合 ⁄ 共 4817字 ⁄ 字号 评论关闭
  学习过设计模式的人都知道有一种行为模式叫做Command模式。在Delphi的VCL Framework中也使用到了这种模式,那就是Action模式。

    命令模式使用的目的在于使用对象来封装客户端的请求命令,由于使用以对象封装,因此可以达到下面的效果:

  • 请求对象可结合多态以及虚拟方法来提供更大的弹性;
  • 负责执行请求的目的对象可以和客户端分离,这就表示多个客户端可以发生相同的请求对象,例如菜单或是工具栏按钮都可以发生打开文件的请求,如此一来菜单和工具栏按钮便可以使用相同的请求对象,而负责打开文件的程序代码并不会绑定到单一的菜单项或是工具栏按钮;
  • 由于使用了请求对象,因此不单是图形用户界面可以触发请求,一般的程序代码也可以通过请求对象来执行特定的工作;
  • 由于请求对象可以使用一个完整的类架构来实现,因此可以让客户端使用一致的程序代码格式来触发各种不同的请求。 

    1. Unit Classes;
    2. { TBasicAction }
    3.   TBasicAction = class(TComponent)
    4.   private
    5.     FActionComponent: TComponent;  {控件变量}
    6.     FOnChange: TNotifyEvent;       {三个事件变量}
    7.     FOnExecute: TNotifyEvent;
    8.     FOnUpdate: TNotifyEvent;
    9.     procedure SetActionComponent(const Value: TComponent);{设置Action控件}
    10.   protected
    11.     FClients: TList;
    12.     procedure Change; virtual;
    13.     procedure SetOnExecute(Value: TNotifyEvent); virtual;{设置Execute事件函数}
    14.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
    15.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    16.   public
    17.     constructor Create(AOwner: TComponent); override;
    18.     destructor Destroy; override;
    19.     function HandlesTarget(Target: TObject): Boolean; virtual;
    20.     procedure UpdateTarget(Target: TObject); virtual;
    21.     procedure ExecuteTarget(Target: TObject); virtual;
    22.     function Execute: Boolean; dynamic;  {其中的动态方法Execute可以由TBasicActionLink类或是TBasicActionLink的派生类或是客户端程序代码调用,而该方法则会执行程序员在它的OnExecute事件中编写的事件处理程序。}
    23.     procedure RegisterChanges(Value: TBasicActionLink);
    24.     procedure UnRegisterChanges(Value: TBasicActionLink);
    25.     function Update: Boolean; virtual;
    26.     property ActionComponent: TComponent read FActionComponent write SetActionComponent;
    27.     property OnExecute: TNotifyEvent read FOnExecute write SetOnExecute;
    28.     {Execute事件 属性}
    29.     property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
    30.   end;
    31. { TBasicAction class reference type }
    32.   TBasicActionClass = class of TBasicAction;

      TBasicActionClass = class of TBasicAction;

  1. { TBasicAction }
  2. constructor TBasicAction.Create(AOwner: TComponent);
  3. begin
  4.   inherited Create(AOwner);
  5.   FClients := TList.Create;
  6. end;
  7. destructor TBasicAction.Destroy;
  8. begin
  9.   inherited Destroy;
  10.   if Assigned(ActionComponent) then
  11.     ActionComponent.RemoveFreeNotification(Self);
  12.   while FClients.Count > 0 do
  13.     UnRegisterChanges(TBasicActionLink(FClients.Last));
  14.   FreeAndNil(FClients);
  15. end;
  16. function TBasicAction.HandlesTarget(Target: TObject): Boolean;
  17. begin
  18.   Result := False;
  19. end;
  20. procedure TBasicAction.ExecuteTarget(Target: TObject);
  21. begin
  22. end;
  23. procedure TBasicAction.Notification(AComponent: TComponent;
  24.   Operation: TOperation);
  25. begin
  26.   inherited Notification(AComponent, Operation);
  27.   if (Operation = opRemove) and (AComponent = ActionComponent) then
  28.     FActionComponent := nil;
  29. end;
  30. procedure TBasicAction.UpdateTarget(Target: TObject);
  31. begin
  32. end;
  33. function TBasicAction.Execute: Boolean;
  34. begin
  35.   if Assigned(FOnExecute) then
  36.   begin
  37.     FOnExecute(Self);
  38.     Result := True;
  39.   end
  40.   else Result := False;
  41. end;
  42. function TBasicAction.Update: Boolean;
  43. begin
  44.   if Assigned(FOnUpdate) then
  45.   begin
  46.     FOnUpdate(Self);
  47.     Result := True;
  48.   end
  49.   else Result := False;
  50. end;
  51. procedure TBasicAction.SetOnExecute(Value: TNotifyEvent);
  52. var
  53.   I: Integer;
  54. begin
  55.   if (TMethod(Value).Code <> TMethod(OnExecute).Code) or
  56.      (TMethod(Value).Data <> TMethod(OnExecute).Data) then
  57.   begin
  58.     for I := 0 to FClients.Count - 1 do
  59.       TBasicActionLink(FClients[I]).SetOnExecute(Value);
  60.     FOnExecute := Value;
  61.     Change;
  62.   end;
  63. end;
  64. procedure TBasicAction.Change;
  65. begin
  66.   if Assigned(FOnChange) then FOnChange(Self);
  67. end;
  68. procedure TBasicAction.RegisterChanges(Value: TBasicActionLink);
  69. begin
  70.   Value.FAction := Self;
  71.   FClients.Add(Value);
  72. end;
  73. procedure TBasicAction.UnRegisterChanges(Value: TBasicActionLink);
  74. var
  75.   I: Integer;
  76. begin
  77.   for I := 0 to FClients.Count - 1 do
  78.     if FClients[I] = Value then
  79.     begin
  80.       Value.FAction := nil;
  81.       FClients.Delete(I);
  82.       Break;
  83.     end;
  84. end;
  85. procedure TBasicAction.SetActionComponent(const Value: TComponent);
  86. begin
  87.   if FActionComponent <> Value then
  88.   begin
  89.     if Assigned(FActionComponent) then
  90.       FActionComponent.RemoveFreeNotification(Self);
  91.     FActionComponent := Value;
  92.     if Assigned(FActionComponent) then
  93.       FActionComponent.FreeNotification(Self);
  94.   end;
  95. end;

对于TBasicAction的派生类而言,例如处理Paste动作的TEditPase类,就可以改写HandlerTarget虚方法,并且在其中编写执行粘贴的程序代码。

  1. TEditPaste = class(TEditAction)
  2.   public
  3.     procedure UpdateTarget(Target: TObject); override;
  4.     procedure ExecuteTarget(Target: TObject); override;
  5.   end;
  6. { TEditPaste }
  7. procedure TEditPaste.ExecuteTarget(Target: TObject);
  8. begin
  9.   GetControl(Target).PasteFromClipboard;
  10. end;
  11. procedure TEditPaste.UpdateTarget(Target: TObject);
  12. begin
  13.   Enabled := Clipboard.HasFormat(CF_TEXT);
  14. end;

 因此,当我们要使用Action设计模式时,可以编写TBasicAction的派生类,并且改写ExecuteTarget虚方法,就像上面提到的TEditPaste类一样。

或是实现企业逻辑程序代码并且把它指定给TBasicAction类的OnExecute事件,然后再调用Execute虚方法。

 我们通过继承TBasicAction类实现了请求对象类,那么如何将客户端与这些请求对象建立关联呢?这里就用到了TBasicActionLink。

 

咱们明天接着说------>

抱歉!评论已关闭.