Les classes génériques
Dans le processus de développement de l’UDR, il est nécessaire pour chaque procédure, fonction ou déclencheur externe d’écrire votre classe créant une instance de l’UDR. Cette tâche peut être simplifiée en écrivant des classes généralisées à l’aide de ce que l’on appelle les génériques. Ils sont disponibles à partir de Delphi 2009, en Free Pascal à partir de la version FPC 2.2.
Note
|
En |
Considérons les deux principaux cas pour lesquels des usines généralisées seront écrites :
-
les copies de procédures, de fonctions et de déclencheurs externes ne nécessitent pas d’informations sur les métadonnées, ni d’actions spéciales dans la logique de création des copies UDR ; des structures fixes sont utilisées pour travailler avec les messages ;
-
Les corps des procédures, fonctions et déclencheurs externes ont besoin d’informations sur les métadonnées, des actions spéciales ne sont pas nécessaires dans la logique de création des copies UDR, et les instances de messages
IMessagemetadata
sont utilisées pour travailler avec les messages.
Dans le premier cas, il suffit de créer la copie désirée de la classe dans la méthode Newitem
sans actions supplémentaires.Pour ce faire, nous utiliserons la restriction du concepteur dans les classes IUdrFunctionFactoryImpl
, IUdrProcedureFactoryImpl
, IUdrTriggerFactoryImpl
. Les annonces de telles classes sont les suivantes :
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
// Une simple classe de fonctions externes
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;
// Une simple procédure externe
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;
// Une simple classe de déclencheur externe
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;
Dans la section d’implémentation, le corps de la méthode setup
peut être laissé vide, rien n’y est fait, dans le corps de la méthode dispose
, il suffit d’appeler le destructeur. Et dans le corps de la méthode Newitem
, il suffit d’appeler le designer par défaut pour le type de substitution 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;
Dans le premier cas, vous ne pouvez pas écrire des classes pour chaque procédure, fonction ou déclencheur. Au lieu de cela, enregistrez-les avec des classes génériques comme suit :
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr;
AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl;
begin
// inscrire notre fonction
AUdrPlugin.registerFunction(AStatus, 'sum_args',
TFunctionSimpleFactory<TSumArgsFunction>.Create());
// inscrire notre procédure
AUdrPlugin.registerProcedure(AStatus, 'gen_rows',
TProcedureSimpleFactory<TGenRowsProcedure>.Create());
// inscrire notre déclencheur
AUdrPlugin.registerTrigger(AStatus, 'test_trigger',
TTriggerSimpleFactory<TMyTrigger>.Create());
theirUnloadFlag := AUnloadFlagLocal;
Result := @myUnloadFlag;
end;
Le second cas est plus compliqué. Par défaut, les informations sur les métadonnées ne sont pas transmises dans les copies des procédures, des fonctions et des déclencheurs. Cependant, les métadonnées sont transmises en tant que paramètre dans la méthode des classes newitem
. Les métadonnées UDR ont le type IRoutineMetadata
, dont le cycle de vie est contrôlé par le moteur Firebird lui-même, de sorte qu’elles peuvent être transférées en toute sécurité dans les copies UDR. A partir de là, vous pouvez obtenir des copies des interfaces pour les messages d’entrée et de sortie, les métadonnées et le type de déclencheur, le nom de l’UDR, le paquetage, les points d’entrée et le corps de l’UDR. Les classes elles-mêmes pour l’implémentation des procédures externes, des fonctions et des déclencheurs n’ont pas de champs pour stocker les métadonnées, nous devrons donc les hérités.
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
...
// Fonction externe avec métadonnées
TExternalFunction = class(IExternalFunctionImpl)
Metadata: IRoutineMetadata;
end;
// Procédure externe avec métadonnées
TExternalProcedure = class(IExternalProcedureImpl)
Metadata: IRoutineMetadata;
end;
// Déclencheur externe avec métadonnées
TExternalTrigger = class(IExternalTriggerImpl)
Metadata: IRoutineMetadata;
end;
Dans ce cas, vos propres procédures stockées, fonctions et triggers doivent être hérités de nouvelles classes avec des métadonnées.
Déclarons maintenant les classes qui créeront l’UDR et initialiseront les métadonnées.
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
...
// Classe de fonctions externes avec métadonnées
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;
// Classe de procédures externes avec métadonnées
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;
// Classe de déclencheurs externes avec métadonnées
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;
L’implémentation de la méthode newitem
est triviale et est similaire au premier cas, sauf qu’il est nécessaire d’initialiser le champ avec AMetadata
.
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;
Un module prêt à l’emploi avec des classes génériques peut être téléchargé à l’adresse suivante https://github.com/sim1984/udr-book/blob/master/examples/Common/UdrFactories.pas.