Templates/fr

From Lazarus wiki

English (en) français (fr) русский (ru)

Introduction

Les patrons sont un mécanisme simple pour résoudre l'écriture de code dupliqué et pour mettre en oeuvre une classe générale pour un type particulier dans un langage fortement typé. Il est utilisé pour les types de base (non objet) ; pour les objets, l'héritage depuis la classe de base TObject (racine de la hiérarchie de classe) est souvent employé pour construire des classes générales (par exemple TObjectList). Dans des langages avancés, les patrons sont remplacés par une implémentation native des prétendus “génériques.” L'implémentation native des génériques dans FPC 2.4.0 est encore ni complète ni utilisable dans la pratique.

Pour:

  • Sûreté du typage – élimine l'ennui des conversions de type et les erreurs qu'il cause souvent.
  • Meilleur maintenabilité – la classe générique est écrite une seule fois et utilisée dans toutes ses classes spécialisées.
  • Meilleure réutilisation de code – Il est possible de créer un bon ensemble de classes génériques réutilisables.
  • Meilleure efficacité, scalabilités – les types de classe générique peuvent être mieux ajusté à un besoin particulier. E.g., sur une machine 16-bit, une TList spécialisée avec un index de type Smallint 16-bit peutr être utilisé.

Contre:

  • Plus complexe
  • Un code plus gros – chaque classe spécialisée est compilée séparément et produit du code supplémentaire.

Usage

L'exemple classique d'une classe générique de base est une liste d'articles. Free Pascal offre trois façons d'utilise les listes:

  • array of – la structure classique de tableau dynamique qui peut tenir des articles d'un même type. Les articles sont adressés directement: UnTableau[Index]. Les fonctions SetLength et Length sont là pour manipuler la taille.
  • TList – manière orienté-objet dans laquelle la classe manipule toutes les opérations de liste. L'implémentation dans la LCL conserve la compatibilité avec la VCL, où la TList contient une liste de pointeurs. Si une liste d'articles d'un type différent est souhaité, une classe complètement nouvelle doit être copiée et ré-écrite ou une conversion de type doit être utilisée à chaque endroit où un article de la liste est référencé. La conversion de type n'est pas efficace —e.g., Pointer(Byte)— et pas sûre en typage: Pointer(Int64). Un index de TListest de type entier. Si des Int64 ou des Smallint sont voulus alors la classe doit être copiée et ré-écrite.
  • TCollection – est plus générique et de typage sûr mais une solution plus lourde pour le stockage d'une liste d'articles. Dans cette approche, le programmeur doit créer une classe article qui hérite de TCollectionItem et placer cette classe nouvellement créée en paramètre de constructeur de TCollection.
  • génériques natifs – pas encore d'implémentation complète. Il manque le support des référence de type générique, l'héritage, les contraintes.
  • templates – utilise des inclusions de code pour contourner le manque de support natif. Peut utiliser l'héritage et les références de type génériques.

Les contraintes sont données par les opérateurs et autres fonctions dans la classe générique.

Les patrons tentent de résoudre le problème en utilisant le concept des fichiers d'inclusion paramétrés. Une unité générique est écrite une seule fois, comme un patron avec l'utilisation de types génériques (non spécifié) en même temps dans les sections interface et implementation. Ainsi dans le processus de spécialisation, un patron de classe est utilisé et les paramètres généraux du patron sont remplacés par les types spécifiques pour créer une nouvelle classe spécialisée.

GenericList.inc:

{$IFDEF INTERFACE}     
  // TGList<T> = class
  TGList = class
    Items: array of T;
    procedure Add(Item: T);
  end;
{$UNDEF INTERFACE}     
{$ENDIF}     

{$IFDEF IMPLEMENTATION}     
procedure TGList.Add(Item: T);
begin
  SetLength(Items, Length(Items) + 1);  
  Items[Length(Items) - 1] := Item;
end;
{$UNDEF IMPLEMENTATION}     
{$ENDIF}

Dans le processus de spécialisation, les fichiers patron créés auparavant sont inclus dans la nouvelle unité.

ListInteger.pas:

unit ListInteger;

interface

uses
  Classes;

type
  T = Integer; // T is specified to some exact type
{$DEFINE INTERFACE}
{$INCLUDE 'GenericList.inc'}

type
  // TListInteger<Integer> = class
  TListInteger = class(TGList)
    // Additional fields and methods can be added here
  end;

implementation

{$DEFINE IMPLEMENTATION}
{$INCLUDE 'GenericList.inc'}

end.

Au final, nous avons une nouvelle unité spécialisée appelée ListInteger que nous pouvons utiliser dans du code.

program GenericTest;
{$mode ObjFPC}
uses
  ListInteger;
var
  List: TListInteger;
begin
  try
    List := TListInteger.Create;
    List.Add(1);
    List.Add(2);
    List.Add(3);
  finally
    List.Free;
  end;
end.

Héritage

Les classes génériques pourraient hériter les unes des autres comme le font les classes non génériques. L'implémentation est un peu plus difficile étant donné qu'un seul type spécialisé d'un classe particulière peut être définie par unité. Pour être capable de spécifier une classe génériques qui hérite d'une autre classe générique, certaines conventions de nommage devrait être observées.

Supposons que nous voulons créer une liste perfectionnée TAdvancedList qui a un champ Capacity en plus.

La définition de la classe de base: GenericList.inc:

{$IFDEF INTERFACE}     
  // TGList<TListIndex, TListItem> = class
  TGList = class
    Items: array[TListIndex] of TListItem;
    procedure Add(Item: TListItem);
  end;
{$UNDEF INTERFACE}
{$ENDIF}

{$IFDEF IMPLEMENTATION}
procedure TGList.Add(Item: TListItem);
begin
  SetLength(Items, Length(Items) + 1);  
  Items[Length(Items) - 1] := Item;
end;
{$UNDEF IMPLEMENTATION}
{$ENDIF}

Définition pour la classe générique améliorée: GenericAdvancedList.inc:

{$IFDEF INTERFACE}     
  TListIndex = TAdvancedListIndex;
  TListItem = TAdvancedListItem;
{$DEFINE INTERFACE}     
{$INCLUDE 'GenericList.inc'}
  // TGAdvancedList<TAdvancedListIndex, TAdvancedListItem> = class(TGList)
  TGAdvancedList = class
    Capacity: TAdvancedListIndex;
  end;
{$UNDEF INTERFACE}
{$ENDIF}

{$IFDEF IMPLEMENTATION}
{$INCLUDE 'GenericList.inc'}
{$UNDEF IMPLEMENTATION}
{$ENDIF}

Maintenant, un exemple de spécialisation: AdvancedListInteger.pas:

unit AdvancedListInteger;

interface

uses
  Classes;

type
  TAdvancedListIndex = Integer; // spécifié dans un type exact
  TAdvancedListItem = Integer; // spécifié dans un type exact
{$DEFINE INTERFACE}
{$INCLUDE 'GenericAdvancedList.inc'}

type
  // TAdvancedListInteger<Integer, Integer> = class
  TAdvancedListInteger = class(TGAdvancedList)
    // Des champs et méthodes supplémentaires peuvent être ajoutés ici.
  end;

implementation

{$DEFINE IMPLEMENTATION}
{$INCLUDE 'GenericAdvancedList.inc'}

end.

Maintenant, nous avons une classe spécialisée depuis la classe générique héritée d'une autre classe générique.

Contraintes

Il est bon d'avoir des classes qui par exemple fonctionnent comme des conteneurs d'autres types. Une classe conteneur n'a pas besoin de connaître quoi que ce soit de la classe contenue. Mais certaines classes pourraient bénéficier de la connaissance de ce qui peut être fait avec la classe contenue. Dans l'implémentation natives des génériques, il y a la possibilité de restreindre le groupe des types utilisables comme paramètres de la classe générique. Dans les patrons, il n'y a pas une telle caractéristique mais les contraintes sont simplement réalisée par l'usage de type génériques dans la classe générique. Si le type générique est utilisé pour l'addition ou la soustraction, alors les seuls types qui supportent ces opérations sont supportés. Les chaînes de caractères peuvent être concaténées en employant l'opérateur d'addition aussi. Mais si la multiplication est employée alors les seuls types numériques sont permis. Si un type générique est utilisé comme un index de tableau alors seuls les types ordinaux peuvent être utilisés. Si la méthode Free est utilisée sur le type générique alors la contrainte inclut toutes les classes. Donc dans les patrons, les opérations réalisées sur le type générique limite l'ensemble des des types utilisables pour la spécialisation.

Groupes contraints théoriques:

  • types ordinaux
  • types flottants
  • enregistrements
  • tableaux
  • ensembles
  • interfaces
  • procédure, fonction, constructeur
  • classes et tous les descendants.

Classes génériques

Il y a plusieurs classes qui gagneraient à être génériques:

  • TList - liste d'articles, Add, Delete, Insert, Move, Exchange, Clear, Sort,
  • TDictionary - liste de paires clé et valeurs,
  • TPair - Clé et valeur pour utiliser dans TDictionary
  • TStack - Structure LIFO, Push et Pop
  • TQueue - Structure FIFO, Enqueue et Dequeue
  • TRange ou TInterval - structure à deux valeurs, Distance
  • TSet - ensemble d'articles, Add, Remove, Intersection, Complement, Union, Product
  • TStream - divisé en TInputStream et TOutputStream
  • TTree - Structure hiérarchique de noeuds
  • TMatrix - Liste multidimensionnelle
  • TBitmap - TMatrix étendue avec des méthodes orientées vers le graphique
  • TGraph
  • TPoint - pourrait être 1D, 2D, 3D or multidimensionnel
  • TVector
  • TComplexNumber

Il y a quelques types spécialisés qui peuvent remplacer les types non génériques:

classe générique spécialisée Type de donnée similaire
TList<Char> string
TList<WideChar> WideString
TList<Byte> array of Byte
TList<string> Classes.TStrings
TList<Pointer> Classes.TList
TList<Boolean> Classes.TBits
TList<TObject> Contnrs.TObjectList
TList<TComponent> Contnrs.TComponentList
TList<TClass> Contnrs.TClassList
TStack<Pointer> Contnrs.TStack
TStack<TObject> Contnrs.TObjectStack
TQueue<Pointer> Contnrs.TQueue
TQueue<TObject> Contnrs.TObjectQueue
TStream<Byte> Classes.TMemoryStream
TDictionary<TPair<string, string>> Classes.TStringList
TList<TMethod> LCLProc.TMethodList
TTree<TTreeNode> ComCtrls.TTreeView
TPoint<Integer> Classes.TPoint
TPoint<SmallInt> Classes.TSmallPoint
TRectangle<Integer> Classes.TRect

Bibliothèques génériques existantes

Tout programmeur peut construire ses propres bibliothèques de classes génériques.

Quelques classes prêtes à l'emploi:

  • TemplateGenerics - Paquet Lazarus de quelques classes génériques expérimentales. En plus du type paramétré pour les valeurs de liste, il offre aussi l'index en type paramétré.

Le dernier paquet peut être téléchargé depuis le référentiel svn en utilisant un client svn.

svn co http://svn.zdechov.net/svn/PascalClassLibrary/Generics/TemplateGenerics/ TemplateGenerics

Voir aussi

Liens externes