2015-04-17 3 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; 

ГИП:
Во время выполнения я хочу, чтобы заполнить список списка с названием «Доступные задачи» с именем 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

Зарегистрируйте каждый класс задач на заводе (раздел 'initialization' устройства является идеальным местом). – 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 позволяет получить список зарегистрированных классов. Это было бы полезно при попытке заполнить ваш пользовательский интерфейс именами зарегистрированных классов.
  • Вам понадобится один экземпляр реестра.
+1

Я был ['достаточно близко'] (http://pastebin.com/5XMWNQAm) к тому же, просто, почему вы выбрали имя' .. .Registry'? [удалит это позже] – TLama

+0

@TLana Какое имя вы бы выбрали? –

+6

'Фабрика' ...... – TLama

Смежные вопросы