Http 我如何重构这个基类并拆分它的功能?

Http 我如何重构这个基类并拆分它的功能?,http,delphi,inheritance,multiple-inheritance,tobjectlist,Http,Delphi,Inheritance,Multiple Inheritance,Tobjectlist,我有一个从TObjectList继承的基类TBuilder。TBuilder可以执行与ADO相关的操作,并用结果填充其内部结构。除此之外,还可以通过HTTP调用在web上执行相同的操作。还将解析返回的结果并更新内部结构 从这里开始,我从数据库表生成pas文件,以模拟其结构。假设我有一个名为Company的表,我会以编程方式生成一个TCompany对象,该对象也继承自TBuilder,然后TBuilder可以选择它需要什么。目前,我使用一种类型构造TCompany,该类型表示我希望它执行ADO操作

我有一个从TObjectList继承的基类TBuilder。TBuilder可以执行与ADO相关的操作,并用结果填充其内部结构。除此之外,还可以通过HTTP调用在web上执行相同的操作。还将解析返回的结果并更新内部结构

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

现在我要做的是将TBuilder拆分,这样一个单元知道如何通过ADO查询数据库,另一个单元知道如何通过HTTP查询数据库。我还想从TBuilder继承这两个类。但我对TCompany提出了挑战,因为它需要继承TBuilder、TADOBuilder或TDSBuilder中的任何一个,后两个是新的单元。如果我从TADOBuilder继承,它只能表示一种类型的对象。我正努力做到这一点,使T公司在任何时候都可以成为这两家公司中的任何一家。我看到您只能通过接口实现多重继承,但我对这一点还不熟悉,还没有弄清楚如何重新设计它,使我的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;
这就是加载过程的样子,此时我正试图以一种更智能的方式将其拆分为单独的单元:

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;
如果我更改类型,它将直接查询数据库。

选项1

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

选择2

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

编辑

下面是使用选项1时的情况

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添加类似的类

通过一些示例可以更容易地提供帮助code@DaveNottage,我添加了一些代码来说明我在做什么Tank you@RM。对于选项。我理解选项1,它可能会起作用,我将进一步研究这个想法。我真的不确定如何实现选项二,因为我试图通过为每个重构类提供两个接口来处理接口,并且这些接口具有适当的加载和插入过程等,但我仍然面临着TCompany问题,因为我无法同时拥有这两个接口。也许我可以通过让FBuilder作为接口,将这个想法与您的选项一结合起来,然后遵循这条路线?考虑到选项1,我认为在TCompany上使用FBuilder不会更好,因为TCompany是生成的,所以我仍然绑定到它的特定实例,无论是TADOBuilder还是TDSBuilder。我希望它能够改变?将它传递给TCompany的构造函数是的,这就是我理解你的意思。我最初认为您希望FBuilder成为TDSBuilder或TADOBuilder。让我想一想,thanx。我稍后再回复。这是一个很好的解决方案,谢谢。我在某种程度上玩弄了这个概念,我不喜欢的是,如果我想在TCompany上使用基类,我必须重新声明基类上可用的许多函数和过程。我仍然不知道如何在这两种类型之间切换我的功能。我的想法是,当我需要时,TCompany可以访问ADO端或Datasnap端。
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;