2015-04-17 8 views
0

我有一個類TTask我從中得到許多其他任務:如何設計一個'開放'類的列表?

TTask= class(TObject) 
    TaskName: string; 
    TaskDescription: string; 
end; 

TTask1= class(TTask) 
TTask2= class(TTask) 
TParser= class(TTask) 
etc... 
(each class implemented in its own unit) 

每個子任務都有自己的名稱和說明:

constructor TParser.Create; 
begin 
inherited;  
TaskName := 'Parser'; 
TaskDescript:= 'Parses a result file'; 
end; 

的GUI:
在運行時我想填充一個名爲「可用任務」的列表框名單,名稱爲TTask1,TTask2等。我想讓用戶將任務從「可用」拖放到「當前任務」列表框中。

在運行時填充可用列表框,因爲TTask1,TTask2沒有實例化,所以使用類var類來獲取TaskName可能會很明智。否則,如果我更改了任務的名稱,我將不得不在兩處更改代碼。

如果可能的話,我應該可以在完成代碼後,輕鬆地將更多任務添加到「可用」列表框中。

做這樣一個動態設計的最好方法是什麼?

什麼我不喜歡的大多是這樣的設計:

var Task: TTask; 
... 
if lstAvailTasks.SelectedItem= 'Parser' then 
    begin 
    Task:= TTask1.Create; 
    Tasks.Add(Task) 
    end 
else 
    etc 
+2

註冊每個任務類到您的工廠(本單元的「初始化」部分是理想的地方)。 – TLama

+0

註冊意味着將它們添加到列表,字典等。 –

回答

4

您正在尋找任務類的註冊。沿着這些路線的東西:

type 
    ETaskClassNotRecognised = class(Exception); 

    TTask = class 
    public 
    constructor Create; virtual; abstract; 
    class function TaskTypeName: string; virtual; abstract; 
    end; 
    TTaskClass = class of TTask; 

    TTaskClassRegistry = class 
    private 
    FRegistry: TDictionary<string, TTaskClass>; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure RegisterTaskClass(TaskClass: TTaskClass); 
    function RegisteredTaskClasses: TArray<TTaskClass>; 
    function CreateTask(const Name: string): TTask; 
    end; 

constructor TTaskClassRegistry.Create; 
begin 
    inherited; 
    FRegistry := TDictionary<string, TTaskClass>.Create; 
end; 

destructor TTaskClassRegistry.Destroy; 
begin 
    FRegistry.Free; 
    inherited; 
end; 

procedure TTaskClassRegistry.RegisterTaskClass(TaskClass: TTaskClass); 
begin 
    FRegistry.Add(TaskClass.TaskTypeName, TaskClass); 
end; 

function TTaskClassRegistry.RegisteredTaskClasses: TArray<TTaskClass>; 
begin 
    Result := FRegistry.Values.ToArray; 
end; 

function TTaskClassRegistry.CreateTask(const Name: string): TTask; 
var 
    TaskClass: TTaskClass; 
begin 
    if not FRegistry.TryGetValue(Name, TaskClass) then begin 
    raise ETaskClassNotRecognised.CreateFmt(
     'No task class named ''%s'' has been registered.', 
     [Name] 
    ); 
    end; 
    Result := TaskClass.Create; 
end; 

注:

  • 我添加了一個虛擬的構造函數的任務類。當你使用元類來實例化對象時,這總是需要的。
  • 我將任務類型名稱添加爲任務類型的虛擬方法。這允許類型名稱被集中並且只提及一次。
  • 註冊表由封裝的字典管理。
  • RegisteredTaskClasses方法允許您獲取註冊類的列表。當試圖用註冊類的名稱填充你的UI時,這會很有用。
  • 您需要註冊表的單個實例。
+1

我一直['足夠接近'](http://pastebin.com/5XMWNQAm)到相同,只是,你爲什麼選擇名稱.. .. .Registry'? [稍後將刪除] – TLama

+0

@TLana你會選擇什麼名字? –

+6

'工廠'...... – TLama