Как мне провести рефакторинг этого базового класса и разделить его функциональность? - PullRequest
0 голосов
/ 07 января 2019

У меня есть базовый класс TBuilder, который наследуется от TObjectList. TBuilder может выполнять операции, связанные с ADO, и заполнять его внутреннюю структуру результатами. В дополнение к этому, та же самая операция может быть сделана через Интернет, через вызовы HTTP. Возвращенные результаты также анализируются, а внутренняя структура обновляется.

С этого момента я генерирую файлы pas из таблицы базы данных, чтобы имитировать ее структуру. Скажем, у меня есть таблица с именем Company, я программно генерирую объект TCompany, который также наследуется от TBuilder, который затем может выбрать, каким он должен быть. На данный момент я создаю TCompany с типом, который говорит, что я хочу, чтобы он выполнял операции ADO или я хотел, чтобы он выполнял операции HTTP. TBuilder обычно будет иметь процедуру загрузки и, в зависимости от типа, будет генерировать SQL и загружаться из базы данных (или http) и заполнять себя результатами.

Теперь я пытаюсь разделить TBuilder так, чтобы один модуль знал, как запрашивать базу данных через ADO, а другой - через HTTP. Я думал также унаследовать эти два класса от TBuilder. Но меня бросает вызов TCompany, потому что он должен наследоваться от TBuilder, или TADOBuilder, или 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;

Вот так выглядит процедура загрузки, в тот момент, когда я пытаюсь разбить ее на отдельные блоки более умным способом:

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;

Если я изменю тип, он напрямую запросит базу данных.

1 Ответ

0 голосов
/ 07 января 2019

Вариант 1)

Не наследовать TCompany от TBuilder. Добавьте поле / свойство FBuilder: TBuilder в TCompany и задайте для него экземпляр TADOBuilder или TDSBuilder. А затем добавьте необходимые методы в TCompany, и эти методы должны будут вызвать требуемый метод в FBuilder. Конечно, необходимые методы должны быть объявлены как виртуальные в TBuilder, и TADOBuilder должен их переопределить.

Option2)

Отделите ваш бизнес-объект (TCompany) от постоянного кода (TBuilder, TADOBuilder). Трудно дать конкретный совет, не зная деталей, но идея в том, что ваша TCompany должна быть независимой от постоянного кода. В общем случае вы добавляете все необходимые бизнес-свойства в TCompany (например, имя, адрес) и используете отдельный класс, который загружает данные в TCompany с помощью TBuilder или TADOBuilder и т. Д.

EDIT

Вот как это будет выглядеть с 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

...