学习过设计模式的人都知道有一种行为模式叫做Command模式。在Delphi的VCL Framework中也使用到了这种模式,那就是Action模式。
命令模式使用的目的在于使用对象来封装客户端的请求命令,由于使用以对象封装,因此可以达到下面的效果:
- 请求对象可结合多态以及虚拟方法来提供更大的弹性;
- 负责执行请求的目的对象可以和客户端分离,这就表示多个客户端可以发生相同的请求对象,例如菜单或是工具栏按钮都可以发生打开文件的请求,如此一来菜单和工具栏按钮便可以使用相同的请求对象,而负责打开文件的程序代码并不会绑定到单一的菜单项或是工具栏按钮;
- 由于使用了请求对象,因此不单是图形用户界面可以触发请求,一般的程序代码也可以通过请求对象来执行特定的工作;
- 由于请求对象可以使用一个完整的类架构来实现,因此可以让客户端使用一致的程序代码格式来触发各种不同的请求。
-
- Unit Classes;
- { TBasicAction }
- TBasicAction = class(TComponent)
- private
- FActionComponent: TComponent; {控件变量}
- FOnChange: TNotifyEvent; {三个事件变量}
- FOnExecute: TNotifyEvent;
- FOnUpdate: TNotifyEvent;
- procedure SetActionComponent(const Value: TComponent);{设置Action控件}
- protected
- FClients: TList;
- procedure Change; virtual;
- procedure SetOnExecute(Value: TNotifyEvent); virtual;{设置Execute事件函数}
- 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; {其中的动态方法Execute可以由TBasicActionLink类或是TBasicActionLink的派生类或是客户端程序代码调用,而该方法则会执行程序员在它的OnExecute事件中编写的事件处理程序。}
- 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;
- {Execute事件 属性}
- property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
- end;
- { TBasicAction class reference type }
- TBasicActionClass = class of TBasicAction;
- { 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
- 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的派生类而言,例如处理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。
咱们明天接着说------>