Обобщённые фабрики
В процессе разработки UDR необходимо под каждую внешнюю процедуру,функцию или триггер писать свою фабрику создающую экземпляр это UDR. Этузадачу можно упростить написав обобщённые фабрики с помощью такназываемых дженериков. Они доступны начиная с Delphi 2009, в Free Pascalначиная с версии FPC 2.2.
Note
|
Замечание
В Free Pascal синтаксис создания обобщённых типов отличается от Delphi.Начиная с версии FPC 2.6.0 декларируется совместимый с Delphi синтаксис. |
Рассмотрим два основных случая для которых будут написаны обобщённыефабрики:
-
экземплярам внешних процедур, функций и триггеров не требуются какие-либосведения о метаданных, не нужны специальные действия в логикесоздания экземпляров UDR, для работы с сообщениями используютсяфиксированные структуры;
-
экземплярам внешних процедур, функций и триггеров требуются сведения ометаданных, не нужны специальные действия в логике создания экземпляровUDR, для работы с сообщениями используются экземпляры интерфейсов
IMessageMetadata
.
В первом случае достаточно просто создавать нужный экземпляр класса вметоде newItem
без дополнительных действий. Для этого воспользуемсяограничением конструктора в классах потомках классовIUdrFunctionFactoryImpl
, IUdrProcedureFactoryImpl
,IUdrTriggerFactoryImpl
. Объявления таких фабрик выглядит следующимобразом:
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
// Простая фабрика внешних функций
TFunctionSimpleFactory<T: IExternalFunctionImpl, constructor> = class
(IUdrFunctionFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// Простая фабрика внешних процедур
TProcedureSimpleFactory<T: IExternalProcedureImpl, constructor> = class
(IUdrProcedureFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalProcedure; override;
end;
// Простая фабрика внешних триггеров
TTriggerSimpleFactory<T: IExternalTriggerImpl, constructor> = class
(IUdrTriggerFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
В секции реализации тело метода setup
можно оставить пустым, в нихничего не делается, в теле метода dispose
просто вызвать деструктор. А втеле метода newItem
необходимо просто вызвать конструктор по умолчаниюдля подстановочного типа T
.
implementation
{ TProcedureSimpleFactory<T> }
procedure TProcedureSimpleFactory<T>.dispose;
begin
Destroy;
end;
function TProcedureSimpleFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure;
begin
Result := T.Create;
end;
procedure TProcedureSimpleFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TFunctionFactory<T> }
procedure TFunctionSimpleFactory<T>.dispose;
begin
Destroy;
end;
function TFunctionSimpleFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction;
begin
Result := T.Create;
end;
procedure TFunctionSimpleFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TTriggerSimpleFactory<T> }
procedure TTriggerSimpleFactory<T>.dispose;
begin
Destroy;
end;
function TTriggerSimpleFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalTrigger;
begin
Result := T.Create;
end;
procedure TTriggerSimpleFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AFieldsBuilder: IMetadataBuilder);
begin
end;
Теперь для случая 1 можно не писать фабрики под каждую процедуру,функцию или триггер. Вместо этого регистрировать их с помощью обобщённыхфабрик следующим образом:
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr;
AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl;
begin
// регистрируем нашу функцию
AUdrPlugin.registerFunction(AStatus, 'sum_args',
TFunctionSimpleFactory<TSumArgsFunction>.Create());
// регистрируем нашу процедуру
AUdrPlugin.registerProcedure(AStatus, 'gen_rows',
TProcedureSimpleFactory<TGenRowsProcedure>.Create());
// регистрируем наш триггер
AUdrPlugin.registerTrigger(AStatus, 'test_trigger',
TTriggerSimpleFactory<TMyTrigger>.Create());
theirUnloadFlag := AUnloadFlagLocal;
Result := @myUnloadFlag;
end;
Второй случай более сложный. По умолчанию сведения о метаданных непередаются в экземпляры процедур, функций и триггеров. Однако метаданныхпередаются в качестве параметра в методе newItem
фабрик. Метаданные UDRимеют тип IRoutineMetadata
, жизненный цикл которого контролируется самимдвижком Firebird, поэтому его можно смело передавать в экземпляры UDR.Из него можно получить экземпляры интерфейсов для входного и выходногосообщения, метаданные и тип триггера, имя UDR, пакета, точки входа итело UDR. Сами классы для реализаций внешних процедур, функций итриггеров не имеют полей для хранения метаданных, поэтому нам придётсясделать своих наследников.
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
...
// Внешняя функция с метаданными
TExternalFunction = class(IExternalFunctionImpl)
Metadata: IRoutineMetadata;
end;
// Внешняя процедура с метаданными
TExternalProcedure = class(IExternalProcedureImpl)
Metadata: IRoutineMetadata;
end;
// Внешний триггер с метаданными
TExternalTrigger = class(IExternalTriggerImpl)
Metadata: IRoutineMetadata;
end;
В этом случае ваши собственные хранимые процедуры, функции и триггерыдолжны быть унаследованы от новых классов с метаданными.
Теперь объявим фабрики, которые будут создавать UDR и инициализироватьметаданные.
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
...
// Фабрика внешних функций с метаданными
TFunctionFactory<T: TExternalFunction, constructor> = class
(IUdrFunctionFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// Фабрика внешних процедур с метаданными
TProcedureFactory<T: TExternalProcedure, constructor> = class
(IUdrProcedureFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalProcedure; override;
end;
// Фабрика внешних триггеров с метаданными
TTriggerFactory<T: TExternalTrigger, constructor> = class
(IUdrTriggerFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
Реализация метода newItem
тривиальна и похожа на первый случай, заисключением того, что необходимо инициализировать поле с метаданными.
implementation
...
{ TFunctionFactory<T> }
procedure TFunctionFactory<T>.dispose;
begin
Destroy;
end;
function TFunctionFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction;
begin
Result := T.Create;
(Result as T).Metadata := AMetadata;
end;
procedure TFunctionFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TProcedureFactory<T> }
procedure TProcedureFactory<T>.dispose;
begin
Destroy;
end;
function TProcedureFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure;
begin
Result := T.Create;
(Result as T).Metadata := AMetadata;
end;
procedure TProcedureFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TTriggerFactory<T> }
procedure TTriggerFactory<T>.dispose;
begin
Destroy;
end;
function TTriggerFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalTrigger;
begin
Result := T.Create;
(Result as T).Metadata := AMetadata;
end;
procedure TTriggerFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder);
begin
end;
Готовый модуль с обобщёнными фабриками можно скачать по адресуhttps://github.com/sim1984/udr-book/blob/master/examples/Common/UdrFactories.pas.