Delphi 构造此通用对象创建的正确方法是什么
忽略使用Aurelius框架这一事实,这个问题更多的是关于我需要如何重新调整代码以使通用构造函数注入对这两种类型都有效:Delphi 构造此通用对象创建的正确方法是什么,delphi,dependency-injection,delphi-10-seattle,delphi-10.1-berlin,Delphi,Dependency Injection,Delphi 10 Seattle,Delphi 10.1 Berlin,忽略使用Aurelius框架这一事实,这个问题更多的是关于我需要如何重新调整代码以使通用构造函数注入对这两种类型都有效: 和 另外,忽略子对象在同一个单元中这一事实,我通常会将它们放在它们自己的单元中,但这只会使发布问题变得更容易 我尝试使用工厂方法模式来确定它在运行时应该建立什么类型的连接,这取决于我实例化的对象。 目前,它正在硬编码它在create上期望的链接类型 在本例中,我希望传入一个TModelDatabaseLink,但希望在运行时确定它可能是什么类型的数据库连接,或者数据库连接
和
另外,忽略子对象在同一个单元中这一事实,我通常会将它们放在它们自己的单元中,但这只会使发布问题变得更容易 我尝试使用工厂方法模式来确定它在运行时应该建立什么类型的连接,这取决于我实例化的对象。 目前,它正在硬编码它在create上期望的链接类型 在本例中,我希望传入一个TModelDatabaseLink,但希望在运行时确定它可能是什么类型的数据库连接,或者数据库连接是否来自文件。 是的,我知道我可以取消对FFilename的注释,并使用它来保存文件名版本,但我总是对学习更多内容感兴趣
unit Model.Database.Connection;
interface
uses
System.Classes,
Data.DB,
Aurelius.Drivers.Interfaces,
Aurelius.Engine.DatabaseManager;
type
TModelDatabaseLink<T> = class
private
//FFilename: string;
FConnection: T;
FOwnsConnection: boolean;
OwnedComponent: TComponent;
end;
TModelDatabaseConnection = class abstract
private
FDatabaseLink: TModelDatabaseLink<TCustomConnection>;
FDatabaseManager: TDatabaseManager;
FConnection: IDBConnection;
function CreateConnection: IDBConnection; virtual; abstract;
procedure CreateDatabaseManager;
public
constructor Create(ADatabaseLink: TModelDatabaseLink<TCustomConnection>);
destructor Destroy; override;
property Connection: IDBConnection read FConnection;
end;
TSQLLiteConnection = class(TModelDatabaseConnection)
private
function CreateConnection: IDBConnection; override;
end;
TFireDacConnection = class(TModelDatabaseConnection)
private
function CreateConnection: IDBConnection; override;
end;
implementation
uses
System.SysUtils,
Aurelius.Drivers.Base,
Aurelius.Drivers.SQLite,
Aurelius.Drivers.FireDac,
FireDAC.Stan.Intf, FireDAC.Stan.Option,
FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def,
FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.VCLUI.Wait,
FireDAC.Comp.Client;
{ TModelDatabaseConnection }
constructor TModelDatabaseConnection.Create(ADatabaseLink: TModelDatabaseLink<TCustomConnection>);
begin
FDatabaseLink := ADatabaseLink;
FConnection := CreateConnection;
if Assigned(FConnection) then
CreateDatabaseManager
else
raise Exception.Create('Failed to open database');
end;
procedure TModelDatabaseConnection.CreateDatabaseManager;
begin
FDatabaseManager := TDatabaseManager.Create(FConnection);
end;
destructor TModelDatabaseConnection.Destroy;
begin
FDatabaseManager.Free;
FDatabaseLink.Free;
inherited Destroy;
end;
{ TSQLLiteConnection }
function TSQLLiteConnection.CreateConnection: IDBConnection;
var
LFilename: String;
LAdapter: TSQLiteNativeConnectionAdapter;
begin
//LFileName := FDatabaseLink.FConnection; << needs to be type string
LAdapter := TSQLiteNativeConnectionAdapter.Create(LFilename);
LAdapter.DisableForeignKeys;
Result := LAdapter;
end;
{ TFireDacConnection }
function TFireDacConnection.CreateConnection: IDBConnection;
var
LAdapter: TFireDacConnectionAdapter;
begin
if Assigned(FDatabaseLink.OwnedComponent) then
LAdapter := TFireDacConnectionAdapter.Create(FDatabaseLink.FConnection as TFDConnection, FDatabaseLink.OwnedComponent)
else
LAdapter := TFireDacConnectionAdapter.Create(FDatabaseLink.FConnection as TFDConnection, FDatabaseLink.FOwnsConnection);
Result := LAdapter;
end;
end.
要在父TModelDatabaseConnection中使用抽象的“GetAdapterClass”类型函数,只需在子级中声明适配器的类即可执行以下操作:
LAdapter := GetAdapterClass.Create...
适配器声明的一个示例是
TFireDacConnectionAdapter = class(TDriverConnectionAdapter<TFDConnection>, IDBConnection)
TFireDacConnectionAdapter=class(TDriverConnectionAdapter,IDBConnection)
我以前在编写抽象层以防需要在应用程序中替换Aurelius时做过这件事。我认为处理您想要做的事情的最好方法是使用接口
我在这里复制代码的某些部分,并进行了调整:
TServerType = (stLocal, stFireDac);
IDatabase = interface
function getDatabaseType: TServerType;
property DatabaseType: TServerType read getDatabaseType;
end;
IAurelius = interface (IDatabase)
['{990BB776-2E70-4140-B118-BEFF61FDBDAF}']
function getDatabaseConnection: IDBConnection;
function getDatabaseManager: TDatabaseManager;
property DatabaseConnection: IDBConnection read getDatabaseConnection;
property DatabaseManager: TDatabaseManager read getDatabaseManager;
end;
IAureliusLocal = interface (IAurelius)
['{9F705CC4-6E3B-4706-B54A-F0649CED3A8D}']
function getDatabasePath: string;
property DatabasePath: string read getDatabasePath;
end;
IAureliusFireDac = interface (IAurelius)
// I use this for a memory database but the logic is the same for FireDAC. You need to add the required properties
end;
TAurelius = class (TInterfacedObject, IAurelius)
private
fServerType: TServerType;
fDatabaseConnection: IDBConnection;
fDatabaseManager: TDatabaseManager;
function getDatabaseConnection: IDBConnection;
function getDatabaseManager: TDatabaseManager;
public
constructor Create (const serverType: TServerType);
end;
TAureliusLocal = class (TAurelius, IAureliusLocal)
private
fDatabasePath: string;
function getDatabasePath: string;
public
constructor Create (const databasePath: string);
end;
TAureliusFireDac = class (TAurelius, IAureliusFireDac)
public
constructor Create (const aConnection: TFDConenction); <-- or whatever parameters you need here to initalise the FireDac connection
end;
…你可以这样称呼它:
var
currentDatabase: IAurelius;
begin
currentDatabase:=createAureliusDatabase(stLocal,'c:\....');
end;
处理数据库创建的更好方法是使用具有不同参数的重载函数或匿名方法来避免案例结束分支 您是否正在使用Spring.Persistence适配器?因为这个代码对我来说很熟悉。如果是这样,请查看Spring.Persistence.Core.ConnectionFactory.pas如何为适应的连接注入不同的ctor参数。老实说,这些实际上是来自TMS Aurelius框架的。不过,我也会看看Spring单元,因为我的电脑上也有它们,看看它是否能给我更多的线索。嗨,Stefan。在Spring.Persistence.Adapters.SQLite中,您指定必须传入一个TSQLite数据库,而不仅仅是一个文件名。我在Spring代码中只能找到..\Marshmallow\External\SQLite3下的代码。这是一个“官方”的Spring one吗?适配器总是能通过它们的适配器。但是,由于它们的实现方式不同,被改编者的论点也会有所不同。这是由知道如何解析不同参数的DI容器或我在前面的评论中提到的单元来处理的。至于SQLite3单元,它们有一些bug已经修复,但它们不是官方的,因为我们不为它们提供任何支持、维护或责任。
constructor TAurelius.Create(const serverType: TServerType);
begin
inherited Create;
fServerType:=serverType;
end;
constructor TAureliusLocal.Create (const databasePath: string);
const
databaseFilename = 'test.sqlite';
begin
inherited Create(stLocal);
fDatabasePath:=Trim(databasePath);
try
fDatabaseConnection:=
TSQLiteNativeConnectionAdapter.Create(
TPath.Combine(fDatabasePath, databaseFilename));
except
raise Exception.Create('stLocal database can''t be created');
end;
end;
constructor TAureliusFireDac.Create (const aConnection: TFDConenction);
begin
inherited Create(stFireDac);
// <-- here you initialise the connection like before but for FireDac
end;
function createAureliusDatabase (const serverType: TServerType): IAurelius;
begin
case serverType of
stLocal: result:=TAureliusLocal.Create(path);
stFireDac: result:=TAureliusFireDac.Create(....);
end;
end;
var
currentDatabase: IAurelius;
begin
currentDatabase:=createAureliusDatabase(stLocal,'c:\....');
end;