Игнорируя тот факт, что здесь используется Aurelius Framework, этот вопрос больше о том, как мне нужно перенастроить код, чтобы внедрение универсального конструктора работало для обоих типов:
‹ string >
и
‹ TCustomConnection >
Также игнорируйте тот факт, что дочерние объекты находятся в одном и том же блоке, я бы обычно помещал их в свои собственные, но это просто упрощает публикацию в вопросе.
Я пытаюсь использовать шаблон Factory Method, чтобы определить, какой тип соединения он должен устанавливать во время выполнения в зависимости от того, какой объект я создаю. На данный момент он жестко кодирует тип ссылки, которую он ожидает при создании.
В этом примере я хочу передать 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.
Еще одна вещь, которую я хотел бы сделать, если это возможно, это изменить два творения:
LAdapter := TSQLiteNativeConnectionAdapter.Create(LFilename)
LAdapter := TFireDacConnectionAdapter.Create(FDatabaseLink.FConnection as TFDConnection, FDatabaseLink.OwnedComponent)
использовать абстрактную функцию типа "GetAdapterClass" в родительском TModelDatabaseConnection и просто объявить класс адаптера в дочернем элементе, чтобы сделать что-то вроде:
LAdapter := GetAdapterClass.Create...
Пример объявления адаптера:
TFireDacConnectionAdapter = class(TDriverConnectionAdapter<TFDConnection>, IDBConnection)