menu
  Home  ==>  articles  ==>  colibri_skelettons  ==>  u_c_tlist   

u_c_tlist - John COLIBRI.


1 - Introduction

Le Pascal de WIRTH offre comme structures mémoire de base le tableau et l'enregistrement. Point de liste chaînée, doublement chaînée, triée, de pile.

La plupart des programmeurs utilisaient donc des listes pointées. Les techniques objet ont permis de généraliser ces listes avec les "conteneurs" qui isolaient dans une unité l'architecture de chaînage, et dans une autre unité la "charge utile" de chaque cellule. Pour employer une nouvelle liste, il suffisait de redéfinir un nouveau descendant de la cellule de base, et toutes les opérations d'ajout, balayage etc étaient immédiatement disponibles (pour une syntaxe simple, voir les transparent perfectionnement pascal. Pascalissime avaient abondamment utilisé ce type de techniques.

Avec l'arrivée de Delphi, les nouvelles structures tList et tStringList on rendu les structures chaînées moins cruciales. L'avantage des tList est la facilité d'emploi: elles se manipulent comme un tableau, sans avoir à se préoccuper par la gestion de la taille.

Le seul inconvénient est que le type des cellules est Pointer, et que lorsque nous plaçons des informations dans une structure pointée par ce Pointer, il faut surtyper.

La solution simple consiste à encapsuler la tList dans une classe, ce qui a l'avantage de localiser ce surtypage, les programmes utilisateurs étant vierge de tout surtypage.

Ce type d'encapsulation étant fréquent, nous avons placé la mécanique d'encapsulation dans une unité "squelette" qui peut être réutilisée et enrichie sans avoir à retaper les quatre ou cinq procédures de base.


2 - Utilisation

2.1 - Un exemple simple

Supposons que nous souhaitions manipuler une liste de valeurs numériques entières.

Pour utiliser le squelette u_c_xxx_list:

  • chargez l'unité u_c_xxx_list dans Delphi
  • sauvegardez dans votre répertoire sous u_c_value_list
  • renommez partout "xxx" en "value"
  • ajoutez u_c_value_list à votre projet
  • compilez
Il faut à présent personnaliser les éléments de la liste, en ajoutant à chaque cellule c_value un attribut entier:
  • ajoutez le champ à m_value à c_value
  • modifiez le CONSTRUCTOR et la fonction d'affichage pour pouvoir manipuler des entiers. La classe est à présent la suivante:
    type c_value// one "value"
                Class(c_basic_object)
                  public
                    m_valueInteger;

                    Constructor create_value(p_nameStringp_valueInteger); Virtual;
                    function f_display_valueString;
                    Destructor DestroyOverride;
                end;

    Constructor c_value.create_value(p_nameStringp_valueInteger);
      begin
        Inherited create_basic_object(p_name);
        m_value:= p_value;
      end// create_value

    function c_value.f_display_valueString;
      begin
        Result:= Format('%-10s %5d', [m_namem_value]);
      end// f_display_value

    Destructor c_value.Destroy;
      begin
        InHerited;
      end// Destroy

La classe c_value_list n'a pas besoin d'être modifiée.

Pour utiliser la nouvelle liste:

  • importez u_c_value_list dans votre unité
  • créez la classe c_value_list, ajoutez quelques valeurs et affichez le tout:
    uses u_c_value_list;
    ...
    procedure TForm1.integer_list_Click(SenderTObject);
      begin
        with c_value_list.create_value_list('test_u_c_value_list'do
        begin
          add_value(c_value.create_value('element_a', 11));
          add_value(c_value.create_value('element_b', 12));

          display_value_list;
          Free
        end// with c_value_list
      end;

Connaissant le type de chaque cellule, il peut être souhaitable de faciliter la création d'une cellule, en fournissant à c_value_list les éléments de la nouvelle cellule:

  • ajoutez à c_value_list une méthode ajoutant une cellule avec une String et un Integer. L'unité devient:
    type c_value_list// "value" list
                    Class(c_basic_object)
                      public
                        ... 
                        procedure add_the_value(p_stringStringp_valueInteger);
                      end;
    ... 
    procedure c_value_list.add_the_value(p_stringStringp_valueInteger);
      begin
        m_c_value_list.add(c_value.create_value(p_stringp_value));
      end// add_the_value

Et le programme principal peut à présent ajouter une nouvelle valeur par:

    procedure TForm1.integer_list_Click(SenderTObject);
      begin
        with c_value_list.create_value_list('test_u_c_value_list'do
        begin
          add_the_value('element_c', 13);
          ...
          Free;
        end// with c_value_list 
      end;

2.2 - La classe c_xxx

La définition de la cellule de base c_xxx est:

    type c_xxx// one "xxx"
                Class(c_basic_object)
                  public

                    Constructor create_xxx(p_nameString); Virtual;
                    function f_display_xxxString;
                    Destructor DestroyOverride;
                end;

Pour les données:

  • aucune donnée n'a été introduite (en dehors de m_name qui fait partie de tous nos objets). Ce sont les cellules renommées qui contiendront des attributs
Et pour les méthodes:
  • create_xxx assure la création (le CONSTRUCTOR sera en général modifié pour ajouter les attributs propres à la cellule utilisée, comme vu plus haut)
  • f_display_xxx retourne simplement une String pour permettre les affichages de mise au point
  • le DESTRUCTOR n'a rien à faire puisqu'aucune données n'est allouée dans cette cellule élémentaire

2.3 - La classe c_xxx_list

L'interface est la suivante:

         c_xxx_list// "xxx" list
                    Class(c_basic_object)
                      public
                        m_c_xxx_listtList;

                        Constructor create_xxx_list(p_nameString); Virtual;

                        function f_xxx_countInteger;
                        function f_c_xxx(p_xxxInteger): c_xxx;
                        procedure add_xxx(p_c_xxxc_xxx);
                        procedure display_xxx_list;

                        Destructor DestroyOverride;
                      end;

Pour les données:

  • m_c_xxx_list est la tList. Cet attribut pourrait d'ailleurs être en PRIVATE.
Et pour les méthodes:
  • create_xxx_list alloue la tList
  • f_xxx_count: retourne le nombre d'éléments. Il serait possible d'utiliser m_c_xxx_list.Count directement, mais la création d'une fonction encapsule mieux notre tList: le programme utilisateur devient indépendant de notre choix de mise en oeuvre de liste
  • f_c_xxx: retourne une cellule élémentaire lorsque l'utilisateur fournit l'indice
  • add_xxx: ajoute une cellule créée par l'utilisateur
  • display_xxx_list: affiche tous les éléments

2.4 - Répertoires et Directives de Compilation

L'unité est prévue pour être placée dans:

C:
  programs
    colibri_skelettons
      colibri_helper_skelettons
        classes

Vous pouvez naturellement changer cette organisation par Projet | Options | Directories

Les directives de compilation sont:

  • R+ (vérification des intervalles)
  • S+ (vérification de la pile)
  • pas d'optimisation

3 - Programmation

La classe c_xxx_list applique simplement le principe d'encapsulation:
  • f_xxx_count renvoie tList.Count:
    function c_xxx_list.f_xxx_countInteger;
      begin
        Result:= m_c_xxx_list.Count;
      end// f_xxx_count
  • et f_c_xxx renvoie le pointeur surtypé (c'est LA le réel bénéfice de toute cette unité):
    function c_xxx_list.f_c_xxx(p_xxxInteger): c_xxx;
      begin
        Result:= c_xxx(m_c_xxx_list[p_xxx]);
      end//  f_c_xxx

Notons que nous pourrions effectuer des vérifications d'intervalle dans f_c_xxx, en retournant NIL en cas d'erreur.

Le programme utilisant cette unité est totalement indépendant du fait que c'est une tList qui a été utilisée. Il pourrait être réécrit avec une autre unité employant des listes chaînées (à condition d'ajouter à cette classe les mêmes procédures, et entre autre celle qui compte les cellules ou qui accède à une cellule par son indice).

Allez, je n'ai pas pu résister à la tentation d'ajouter une liste pointée. L'objectif est que le programme principal ne soit pas modifié. Cette liste est dans u_cell_list_with_pointers. Sa programmation est celle d'une liste chaînée classique avec ajout en fin (seules les modifications sont présentées ici):

    type c_cell// one "cell"
                Class(c_basic_object)
                  public
                    m_c_next_cellc_cell;
                    ...
                end;

         c_cell_list// "cell" list
                    Class(c_basic_object)
                      public
                        m_c_root_cellm_c_end_cellc_cell;
                        m_cell_countInteger;
                        ...
                      end;

    function c_cell_list.f_cell_countInteger;
      begin
        Result:= m_cell_count;
      end// f_cell_count

    function c_cell_list.f_c_cell(p_cellInteger): c_cell;
      var l_indexInteger;
      begin
        l_index:= 0;
        Result:= m_c_root_cell;
        while (Result<> niland (l_indexp_celldo
        begin
          Inc(l_index);
          Result:= Result.m_c_next_cell;
        end// while
      end//  f_c_cell

    procedure c_cell_list.add_cell(p_c_cellc_cell);
        // -- add at the END of the list
      begin
        // -- handle first addition differently
        if m_c_root_cellnil
          then m_c_root_cell:= p_c_cell
          else m_c_end_cell.m_c_next_cell:= p_c_cell;
        // -- update m_c_end_cell
        m_c_end_cell:= p_c_cell;

        Inc(m_cell_count);
      end// add_cell

    procedure c_cell_list.display_cell_list;
      var l_c_cellc_cell;
      begin
        display(m_name' 'IntToStr(f_cell_count));

        l_c_cell:= m_c_root_cell;
        while l_c_cell<> nil do
        begin
          display(l_c_cell.f_display_cell);
          l_c_cell:= l_c_cell.m_c_next_cell;
        end// while
      end// display_cell_list

    Destructor c_cell_list.Destroy;
      var l_c_kill_cellc_cell;
      begin
        while m_c_root_cell<> nil do
        begin
          l_c_kill_cell:= m_c_root_cell;
          m_c_root_cell:= m_c_root_cell.m_c_next_cell;
          l_c_kill_cell.Free;
        end// while
        Inherited;
      end// Destroy

Et la troisième procédure de notre projet de test est effectivement identique à celle mettent en oeuvre une tList.


4 - Améliorations

Si les éléments de chaque cellule contiennent des données sans traitement majeur à effectuer sur ces données, il peut être plus simple d'utiliser un RECORD pointé pour chaque cellule. Nous faisons l'économie de l'écriture du CONSTRUCTOR et du DESTRUCTOR. Et bénéficions d'une réduction de consommation mémoire (les octets des tObject, et en plus le champ m_name que nous avons ajouté à tous nos objets).

L'idée d'encapsuler une tList n'est pas nouvelle. Il est vraissemblable que d'autres programmeurs aient proposés des solutions plus sophistiquée que le copier / coller. Un Expert Delphi vient immédiatement à l'esprit. Mais la solution utilisant un squelette a l'avantage de la simplicité.


5 - Télécharger

Vous pouvez télécharger:
  • u_c_tlist.zip: l'unité u_c_tlist seule (4 K)
  • test_u_c_tlist.zip: le projet de test (18 K) qui contient:
    • l'unité
    • toutes les unités qu'elle utilise
    • le projet de démonstration


Comme d'habitude:
  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.

L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Créé: fev-02. Maj: aou-15  148 articles, 471 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2015
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
      – p_memo
      – u_c_tlist
      – u_c_tstringlist
      – p_select_files
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog