通用工厂

Fre*_*red 2 delphi generics factory delphi-2009

假设我有一个TModel:

TModelClass = class of TModel;
TModel = class
  procedure DoSomeStuff;
end;
Run Code Online (Sandbox Code Playgroud)

和2个后代:

TModel_A = class(TModel);
TModel_B = class(TModel);
Run Code Online (Sandbox Code Playgroud)

和工厂:

TModelFactory = class
  class function CreateModel_A: TModel_A;
  class function CreateModel_B: TModel_B;
end;
Run Code Online (Sandbox Code Playgroud)

现在我想重构一下:

TModelFactory = class
  class function CreateGenericModel(Model: TModelClass) : TModel
end;

class function TModelFactory.CreateGenericModel(Model: TModelClass) : TModel
begin
  ...
  case Model of
    TModel_A: Result := TModel_A.Create;
    TModel_B: Result := TModel_B.Create;
  end;
  ...
end;
Run Code Online (Sandbox Code Playgroud)

到目前为止没关系,但每次创建TModel后代时,我都要修改工厂case语句.

我的问题:这可能为我的所有TModel后代创建一个100%的通用工厂,所以每次创建TModel后代我都不需要修改TModelFactory吗?

我尝试使用Delphi 2009泛型,但没有找到有价值的信息,所有都与基本用法TList<T>等有关.

更新 对不起,但也许我不清楚或不理解你的答案(我还是一个菜鸟),但我想要实现的是:

var
  M: TModel_A;
begin
  M: TModelFactory.CreateGenericModel(MY_CONCRETE_CLASS);
Run Code Online (Sandbox Code Playgroud)

mgh*_*hie 6

好吧,你可以写

class function TModelFactory.CreateGenericModel(AModelClass: TModelClass): TModel;
begin
  Result := AModelClass.Create;
end;
Run Code Online (Sandbox Code Playgroud)

但是你不再需要工厂了.通常会有一个不同类型的选择器,如整数或字符串ID,以选择工厂应创建的具体类.

编辑:

要回答你关于如何在不需要更改工厂的情况下添加新类的注释 - 我将为您提供一些适用于非常旧的Delphi版本的简单示例代码,Delphi 2009应该采用更好的方法来实现这一点.

每个新的后代类只需要在工厂注册.可以使用多个ID注册相同的类.代码使用字符串ID,但整数或GUID也可以使用.

type
  TModelFactory = class
  public
    class function CreateModelFromID(const AID: string): TModel;
    class function FindModelClassForId(const AID: string): TModelClass;
    class function GetModelClassID(AModelClass: TModelClass): string;
    class procedure RegisterModelClass(const AID: string;
      AModelClass: TModelClass);
  end;

{ TModelFactory }

type
  TModelClassRegistration = record
    ID: string;
    ModelClass: TModelClass;
  end;

var
  RegisteredModelClasses: array of TModelClassRegistration;

class function TModelFactory.CreateModelFromID(const AID: string): TModel;
var
  ModelClass: TModelClass;
begin
  ModelClass :=  FindModelClassForId(AID);
  if ModelClass <> nil then
    Result := ModelClass.Create
  else
    Result := nil;
end;

class function TModelFactory.FindModelClassForId(
  const AID: string): TModelClass;
var
  i, Len: integer;
begin
  Result := nil;
  Len := Length(RegisteredModelClasses);
  for i := 0 to Len - 1 do
    if RegisteredModelClasses[i].ID = AID then begin
      Result := RegisteredModelClasses[i].ModelClass;
      break;
    end;
end;

class function TModelFactory.GetModelClassID(AModelClass: TModelClass): string;
var
  i, Len: integer;
begin
  Result := '';
  Len := Length(RegisteredModelClasses);
  for i := 0 to Len - 1 do
    if RegisteredModelClasses[i].ModelClass = AModelClass then begin
      Result := RegisteredModelClasses[i].ID;
      break;
    end;
end;

class procedure TModelFactory.RegisterModelClass(const AID: string;
  AModelClass: TModelClass);
var
  i, Len: integer;
begin
  Assert(AModelClass <> nil);
  Len := Length(RegisteredModelClasses);
  for i := 0 to Len - 1 do
    if (RegisteredModelClasses[i].ID = AID)
      and (RegisteredModelClasses[i].ModelClass = AModelClass)
    then begin
      Assert(FALSE);
      exit;
    end;
  SetLength(RegisteredModelClasses, Len + 1);
  RegisteredModelClasses[Len].ID := AID;
  RegisteredModelClasses[Len].ModelClass := AModelClass;
end;
Run Code Online (Sandbox Code Playgroud)


Ond*_*lle 5

Result := Model.Create;
Run Code Online (Sandbox Code Playgroud)

也应该工作.


Too*_*the 5

如果构造函数是虚拟的,则Model.Create的解决方案有效.

如果你使用delphi 2009,你可以使用泛型使用另一个技巧:

type 
  TMyContainer<T: TModel, constructor> (...)
  protected
    function CreateModel: TModel;
  end;

function TMyContainer<T>.CreateModel: TModel;
begin
  Result := T.Create; // Works only with a constructor constraint.   
end;
Run Code Online (Sandbox Code Playgroud)