Как мне провести рефакторинг этого базового класса и разделить его функциональность?

У меня есть базовый класс 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 ответ

Опция 1)

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

Option2)

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

РЕДАКТИРОВАТЬ

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

Другие вопросы по тегам