首页 > 解决方案 > 如何重构这个基类并拆分它的功能?

问题描述

我有一个TBuilder继承自TObjectList. TBuilder可以执行 ADO 相关操作并使用结果填充其内部结构。除此之外,可以通过 HTTP 调用在 Web 上完成相同的操作。返回的结果也会被解析并更新内部结构。

从这里开始,我从数据库表生成 pas 文件以模仿其结构。假设我有一个名为 Company 的表,我以编程方式生成一个TCompany对象,该对象也继承自TBuilder该对象,然后可以选择需要的对象。目前,我TCompany使用一种类型进行构造,该类型表示我希望它执行 ADO 操作或我希望它执行 HTTP 操作。TBuilder然后通常会有一个加载过程,然后根据类型,它会生成 SQL 并从数据库(或 http)加载,并在内部用结果填充自己。

现在我要做的是拆分TBuilder,以便一个单元知道如何通过 ADO 查询数据库,而另一个单元通过 HTTP 查询。我也在考虑继承这两个类TBuilder。但我遇到了挑战,TCompany因为它需要继承自TBuilder, or TADOBuilderor TDSBuilder(后两个是新单位)。如果我继承TADOBuilder它只能代表一种类型的对象。我正在努力做到这一点,以便TCompany随时可以成为两者中的任何一个。我看到您只能使用接口实现多重继承,但我对此并不陌生,无法弄清楚如何重新设计它,以便我TCompany可以同时成为两种类型的对象。

有什么想法可以解决这个问题吗?目前我被困在Delphi 6中。

这是它的外观:

 TCompany = class(TBuilder) //I generate this programatically. This represents a table in the database
      private
        fUser: TSecurityUser;

        function GetCompanyName: TBuilderField;
        function GetCompanyAbbreviation: TBuilderField;
        function GetCompanyID: TBuilderField;
        function GetDateCreated: TBuilderField;
        function GetDeleted: TBuilderField;
      public
        Property CompanyID:TBuilderField read GetCompanyID;
        Property CompanyName:TBuilderField read GetCompanyName;
        Property Abbreviation:TBuilderField read GetCompanyAbbreviation;
        property DateCreated:TBuilderField read GetDateCreated;
        property Deleted:TBuilderField read GetDeleted;

        property User:TSecurityUser read fUser Write fUser;

        constructor NewObject(psCompanyName,psAbbreviation:string);
        constructor Create(conType:TConnectionType = conTypeSQLServer);override;

这就是 Load 过程的样子,此时我正试图以更智能的方式拆分成单独的单元:

function TBuilder.Load(psSQL:string = ''; poLoadOptions:TLoadOptions = []; poConnectionType:TConnectionType = conNone): Boolean;
var
  LoQuery:TADOQuery;
  LoSQL:string;
  LoConnectionType:TConnectionType;
begin
  Result := True;

  LoConnectionType := fConnectionType;
  if poConnectionType <> conNone then
    LoConnectionType := poConnectionType;

  if LoConnectionType = conTypeSQLServer then
  begin
    LoQuery := TADOQuery.Create(nil);

    Try
      try
        LoQuery.Connection := uBuilder.ADOConnection;
        LoSQL := psSQL;
        if LoSQL = '' then
          LoSQL := BuildSelectSQL;
        LoQuery.SQL.Text := LoSQL;
        LoQuery.Open;
        LoadFromDataset(LoQuery);
      except on E:exception do
        begin
          Error := E.Message;
          Result := False;
        end;
      end;
    Finally
      FreeAndNil(LoQuery);
    end;
  end else
  if fConnectionType = conTypeDatasnap then
  begin

    fWebCallType := sqlSelect;
    try

      if Assigned(fParent) then
        if fParent.Error <> '' then
          Exit;

      if Assigned(OnBusyLoadingHook) then
        OnBusyLoadingHook('Busy loading...');

      {Reset the msg}
      if Assigned(OnDisplayVisualError) then
        OnDisplayVisualError(imtRed,'');

      if (poLoadOptions <> LoadOptions) then
        LoadOptions := LoadOptions + poLoadOptions;
      Result := InternalLoad(loFullyRecursiveLoad in LoadOptions);
    finally
      { We're done loading }
      if Assigned(OnBusyLoadingHook) then
        OnBusyLoadingHook('');
    end;

  end;
end;

然后我会以这种方式使用该对象:

var
  LoCompany:TCompany;
begin
  LoCompany := TCompany.Create(conTypeDatasnap);
  LoCompany.CompanyName.Value := 'Test';
  LoCompany.Load; //This will then generate the appropriate JSON and pass it via idhttp component to the server and the results will be parsed into its structure.  
end;

如果我更改类型,它将直接查询数据库。

标签: httpdelphiinheritancemultiple-inheritancetobjectlist

解决方案


选项1)

不要从 TBuilder 继承 TCompany。将 FBuilder: TBuilder 字段/属性添加到 TCompany 并将其设置为 TADOBuilder 或 TDSBuilder 实例。然后将所需的方法添加到 TCompany,这些方法需要在 FBuilder 上调用所需的方法。当然,所需的方法必须在 TBuilder 上声明为虚拟方法,并且 TADOBuilder 需要覆盖这些方法。

选项2)

将您的业务对象(TCompany)与持久性代码(TBuilder、TADOBuilder)分开。在不了解细节的情况下很难给出具体的建议,但想法是您的 TCompany 应该独立于持久性代码。通常,您将所有必需的业务属性添加到 TCompany(例如名称、地址)并使用单独的类,该类使用 TBuilder 或 TADOBuilder 等将数据加载到 TCompany 中。

编辑

这是使用 Option1 时的样子。

TBuilder = abstract class
  procedure Load; virtual;
end;

TADOBuilder = class(TBuilder)
  procedure Load; override;
end;

TDSBuilder = class(TBuilder)
  procedure Load; override;
end;

TCompany = class
private
  FBuilder: TBuilder;
public
  constructor Create(aBuilder: TBuilder);
  procedure Load;
end;

{ TCompany }

constructor TCompany.Create(aBuilder: TBuilder);
begin
  inherited;
  FBuilder := aBuilder;
end;

procedure TCompany.Load;
begin
  FBuilder.Load;
end;

....

选项 2 的编辑示例

TCompany = class
private
  FId: Integer;
  FName: string;
...
public
  property Id: Integer read FId write FId;
  property Name: string read FName write FName;
end;

TADOPerssiter = class
public
  constructor Create(const aConnectionString: string);
  // Creates and loads TCompany from ADO
  function LoadCompany(aId: Integer): TCompany;
  procedure SaveCompany(aCompany: TCompany);
end;

为 DS 添加类似的类


推荐阅读