http - 如何重构这个基类并拆分它的功能?
问题描述
我有一个TBuilder
继承自TObjectList
. TBuilder
可以执行 ADO 相关操作并使用结果填充其内部结构。除此之外,可以通过 HTTP 调用在 Web 上完成相同的操作。返回的结果也会被解析并更新内部结构。
从这里开始,我从数据库表生成 pas 文件以模仿其结构。假设我有一个名为 Company 的表,我以编程方式生成一个TCompany
对象,该对象也继承自TBuilder
该对象,然后可以选择需要的对象。目前,我TCompany
使用一种类型进行构造,该类型表示我希望它执行 ADO 操作或我希望它执行 HTTP 操作。TBuilder
然后通常会有一个加载过程,然后根据类型,它会生成 SQL 并从数据库(或 http)加载,并在内部用结果填充自己。
现在我要做的是拆分TBuilder
,以便一个单元知道如何通过 ADO 查询数据库,而另一个单元通过 HTTP 查询。我也在考虑继承这两个类TBuilder
。但我遇到了挑战,TCompany
因为它需要继承自TBuilder
, or TADOBuilder
or 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;
如果我更改类型,它将直接查询数据库。
解决方案
选项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 添加类似的类
推荐阅读
- mapbox - 倾斜后在悬停时突出显示填充-拉伸特征?
- java - 客户端查找服务器时出现 NotBoundException
- sql - 需要在 SQL Server 2016 中创建周期性问题报告
- xcode - 创建应该在真实设备和模拟器上都可以使用的 .ipa 文件
- javascript - 当我点击 npm run dev 和 npm run serve 命令时遇到问题
- google-cloud-platform - 在 GCP 上使用 SSH 访问 JupyterLab 还不起作用
- python - 按特定顺序从目录打开文件
- javascript - 从 TypeScript 操作 JSON
- web-scraping - 如何以外部用户身份从应用 PowerBI 获取数据
- wordpress - 如何将存储在数据库中的联系表格 7 值链接到特定用户 - Wordpress