在實際的工作中偶爾會遇到如下情況:讓一個類繼承架構的對象實體能夠通過一致的方法執行另外一個目標類對象的方法或是獨立的函數,目標類需要被執行的方法或是獨立的函數可以自由地改變或是增加而會影響執行類的架構。這個時候就會用到ForEach設計模式。
ForEach模式的目的是讓擁有穩定繼承架構的類對象能夠執行外部可變化的方法或是函數。 它適用於以下兩種情況:
- 如果應用系統中已經有一個穩定的繼承架構,而這個類架構須加入新的方法或是需要執行一些方法,但是我們並不想改變繼承架構或是修改類聲明,以避免經常重新編譯或是造成很脆弱的繼承架構
- 一個Collection對象需要對其子項執行許多會改變的函數,而又希望擁有一致的機制讓每一個子項都能夠執行這些外部函數
實現
如上圖,左邊的一個穩定的類繼承架構,在父類中可以定義一個虛擬方法ForEach,這個方法接受一個參數,就是需要執行的外部類方法或是獨立方法。若希望TChild1執行Target中的Routine2這個方法,則可以使用如下代碼
var
aChild: TRoot;
aTarget: Target;
begin
aTarget := Target.Create;
aChild := TChild2.Create;
......
aChild.ForEach(aTarget.Routine2);
......
end;
第二種ForEach設計模式經常使用在Collection管理的動態子項對象中,而且這些對象都需要執行外部可能變化的方法或函數,在這種應用中便可以定義一個外部目標對象,在這個目標對象中有許多的方法,它們需要執行。當特定的方法需要執行時,只要調用Collection定義的ForEach方法,再由Collection一一地要求所有子項順序的執行外部方法即可。
使用舉例
假設需要對窗體中的某類組件執行某些操作(為了方便後面說明,這裡假設對窗體上部分TListView組件進行操作)。則可以使用ForEach模式。首先定義出外部方法的標准模式TRunner,然後再定義一個Manager類,負責管理TListVIEw組件,並在其中定義一個ForEach方法。
Type
TRunner = procedure (AListView: TListVIEw) of Object;
TLVManager = class
private
FList: TList;
public
constructor Create;
destructor Destroy; override;
procedure AddListView(AListView: TListVIEw);
procedure ForEach(ARunner: TRunner);
end;
implementation
procedure TLVManager.AddListView(AListView: TListVIEw);
begin
FList.Add(AListVIEw);
end;
constructor TLVManager.Create;
begin
FList := TList.Create;
end;
descturctor TLVManager.Destroy;
var
i: Integer;
begin
for i := FList.Count - 1 downto 0 do
TListVIEw(FList.Items[i]).Free;
FList.Clear;
FreeAndNil(FList);
inherited;
end;
procedure TLVManager.ForEach(ARunner: TRunner);
var
i: Integer;
begin
for i := 0 to FList.Count - 1 do
ARunner(TListVIEw(FList.Items[i]));
end;
我們將需要執行的外部方法全部定義的TExternalRoutines類中
type
TExternalRoutines = class
public
procedure Routine1(AListView: TListVIEw);
procedure Routine2(AListView: TListVIEw);
end;
當應用程序在執行期間需要所有由TLVManager類管理的ListVIEw執行TExternalRoutines類的某一個方法時就調用TLVManager的ForEach方法。
var
mng: TLVManager;
Runner: TExternalRoutines;
//客戶端程序初始化部分
begin
mng := TLVManger.Create;
//添加ListVIEw到TLVManager中
mng.AddListView(ListVIEw1);
......
Runner := TExternalRoutines.Create;
end;
// 客戶端程序執行部分
begin
mng.ForEach(Runner.Routine1); //對被加入到TLVManager中的ListVIEw執行Routine1方法
end;