Copie du wikimiroir

Ceci est un miroir du wiki de pascal.chat. La modification, l’historique et la connexion se trouvent là-bas.

Templates/fr

English · Français · Русский

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:

Contre:

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:

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:

pascal
{$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:

pascal
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.

pascal
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:

pascal
{$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:

pascal
{$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:

pascal
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:

Classes génériques

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

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:

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

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

Voir aussi

Liens externes


From the Free Pascal wiki, page "Templates/fr", as it stood on 2023-02-18 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Version 1, dernière modification par WikiArchive .