menu
  Home  ==>  articles  ==>  colibri_skelettons  ==>  u_c_tstringlist   

u_c_tstringlist - John COLIBRI.


1 - Introduction

Ce texte explique comment utiliser la classe-type c_stringlist.

Cette classe encapsule la tStringList de base. Cette liste permet de gérer des liste de chaînes de caractères et, accessoirement un pointeur. Traditionnellement le programmeur utilise le pointeur pour désigner:

  • soit une donnée dont la taille ne dépasse pas 4 octets, en surtypant le pointeur
  • soit un RECORD ou autre type Pascal alloué par NEW ou GETMEM et en surtypant le pointeur
  • soit une classe Delphi, en surtypant toujours.
Le programme utilisateur doit donc en permanence surtyper, ce qui est gênant au niveau de la maintenance: la personne qui a créé le programme connaît les précautions à prendre du fait du surtypage, le malheureux qui récupérera le projet six mois plus tard risque de s'arracher les cheuveux.

Traditionnellement la tStringList est alors encapsulée dans une CLASS, qui manipule et pointeur. Nous bénéficions ainsi des avantages de l'encapsulation:

  • le programmeur créant la classe isole les surtypages dans une unité
  • l'utilisateur n'a pas besoin d'en avoir connaissance: son programme manipule les données pointées sans savoir qu'il s'agit d'un surtypage
  • si un problème intervient, tout est proprement traité dans une unité, et non pas réparti dans un programme gigantestque dont ce n'est pas le souci principal
Comme nous utilisons fréquemment ces tStringList, il s'avère que les trois ou quatre procédures de base sont toujours les mêmes: créer l'encapsulateur, ajouter un élément avec sa chaîne et les données annexes, lister le contenu etc.

Nous avons donc crée une unité standard contenant l'ébauche de ce type d'encapsulation. Ne sachant si la classe finale contiendra des noms de répertoires (c_path_list), des noms de fichiers .ZIP (c_zip_list), nous avons utilisé l'abréviatioon xxx que nous remplaçons soit par path, soit par zip.

Nous avons désigné ce type de classe Delphi par le terme de "squelette". Jadis nous utilisions "shell", mais ce terme a une signification toute différente sous Windows. Et Template est aussi déjà utilisé ailleurs. Si vous avez un meilleur nom à proposer, je suis toute ouie à jcolibri@jcolibri.com.


2 - Utilisation

2.1 - Un exemple simple

Supposons que nous souhaitions manipuler une liste de deux chaînes. Par exemple le répertoire et le nom de fichier (pour savoir dans quel répertoire se trouvent nos fichiers).

Pour utiliser le squelette u_c_tstringlist:

  • chargez l'unité u_c_xxx_tstringlist dans Delphi
  • sauvegardez dans votre répertoire sous u_c_file_and_path_list
  • renommez partout "xxx" en "file"
  • renommez partout "yyy" en "path"
  • ajoutez u_c_file_list à votre projet
  • compilez
Comme notre squelette prévois que chaque élément de la liste comportera une String, tout fonctionne déjà:
  • ajoutez à votre projet un tButton
  • créez son événement OnClick et ajoutez quelques couples (chemin / nom):
        with c_file_list.create_file_list('test_u_c_tstringlist'do
        begin
          add_file('pascal_to_html.pas'c_file.create_file('pascal_to_html.pas'Nil'c:\programs\'));
          add_file('site_editor.pas'c_file.create_file('site_editor.pas'Nil'c:\programs\'));

          display_file_list;

          Free;
        end// with c_file_list

Et voici le résultat:

test_u_c_tstringlist 2
    pascal_to_html.pas c:\programs\
    site_editor.pas c:\programs\


2.2 - Un exemple avec calcul de taille

Un tout peu plus compliqué: la liste des fichiers par répertoire, en affichant une seule fois le nom des répertoires et en alignant les noms de fichiers.

Voici ce que nous entrons:

c:\programs\ et pascal_to_html.pas
c:\programs\ et site_editor.pas
c:\utilities\ et http_raw_logs.pas
c:\utilities\ et uses_list.pas

Voici ce que nous souhaitons:

c:\programs\   pascal_to_html.pas
    site_editor.pas
c:\utilities\   http_raw_logs.pas
    uses_list.pas

Pour cela il faut:

  • chargez l'unité u_c_tstringlist dans Delphi
  • sauvegardez dans votre répertoire sous u_c_path_list
  • renommez partout "xxx" en "path"
  • renommez partout "yyy" en "file"
  • ajoutez u_c_path_list à votre projet
  • compilez
Il faut à présent personnaliser l'affichage:
  • pour calculer la taille max des noms de répertoires,
    • ajoutez Math à la liste des USES de l'IMPLEMENTATION de u_c_file_name_list
    • ajoutez le calcul de la chaîne de taille maximale dans le CONSTRUCTOR de chaque cellule:

          Constructor c_path.create_path(p_nameStringp_c_path_listc_path_list;
              p_fileString);
            begin
              Inherited create_basic_object(p_name);

              m_c_parent_path_list:= p_c_path_list;
              m_file:= p_file;

              with m_c_parent_path_list do
                m_path_length_max:= Max(m_path_length_maxLength(p_name));
            end// create_path


  • pour l'affichage formaté:
    • ajoutez la méthode display_the_path_and_files dans la CLASS c_file_list
    • voici le corps de cette méthode:

          procedure c_path_list.display_the_path_and_files;
            var l_path_and_fileInteger;
                l_pathl_previous_pathString;
            begin
              display(m_name' 'IntToStr(f_path_count));

              l_previous_path:= '';
              for l_path_and_file:= 0 to f_path_count- 1 do
                with f_c_path(l_path_and_filedo
                begin
                  l_path:= m_name;
                  if l_pathl_previous_path
                    then l_path:= '';
                  display(Format('%-'IntToStr(m_path_length_max)+ 's ', [l_path])
                    + m_file);
                  l_previous_path:= m_name;
                end// for, with
            end// display_the_path_and_files


2.3 - La classe c_xxx

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

    type c_xxx_listClass// forward

         c_xxx// one "xxx"
                Class(c_basic_object)
                  public
                    // -- not the same name as the tStringList
                    m_c_parent_xxx_listc_xxx_list;
                    m_yyyString;

                    Constructor create_xxx(p_nameStringp_c_xxx_listc_xxx_list;
                        p_yyyString); Virtual;
                    function f_display_xxxString;
                    Destructor DestroyOverride;
                end;

Pour les données:

  • m_yyy: un exemple de donnée stockée dans chaque cellule (en plus de m_name qui appartient à c_basic_object)
  • m_c_parent_xxx_list: un lien de chaque cellule vers la structure à laquelle elle appartient. Les méthodes de chaque cellules peuvent au besoin accéder à des champs propres à la structure (dans l'exemple ci-dessus, la taille maximale des chemins)
Notez que la classe c_xxx_list a été déclarée par anticipation pour pouvoir définir l'attribut m_c_parent_xxx_list.

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.4 - La classe c_xxx_list

L'interface est la suivante:

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

                     Constructor create_xxx_list(p_nameString); Virtual;

                     function f_xxx_countInteger;
                     function f_c_xxx(p_xxxInteger): c_xxx;
                     function f_index_of(p_xxxString): Integer;
                     function f_c_find_by_xxx(p_xxxString): c_xxx;
                     procedure add_xxx(p_xxxStringp_c_xxxc_xxx);
                     procedure add_element(p_xxxp_yyyString);
                     procedure add_unique_xxx(p_xxxp_yyyString);
                     procedure display_xxx_list;

                     Destructor DestroyOverride;
                   end;

Pour les données:

  • m_c_xxx_list est la tStringList. Cet attribut pourrait d'ailleurs être en PRIVATE.
Et pour les méthodes:
  • create_xxx_list alloue la tStringList
  • 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 tStringList: 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
  • f_index_of encapsule m_c_xxx_list.IndexOf
  • f_c_find_by_xxx: retourne la cellule lorsque nous fournissons le nom
  • add_xxx: ajoute une cellule créée par l'utilisateur
  • add_element: ajoute une cellule fournissant le nom et les différents champs à placer dans la cellule (ici un seul)
  • add_unique_xxx: ajoute une cellule en vérifiant que le nom est unique
  • display_xxx_list: affiche tous les éléments
  • Destroy: libère la liste

2.5 - 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 tStringList.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_xxx_indexInteger): c_xxx;
          begin
            Result:= c_xxx(m_c_xxx_list.Objects[p_xxx_index]);
          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.
  • lorsque chaque cellule doit contenir une chaîne nous pouvons:
    • soit utiliser m_name, qui appartient à c_basic_object
    • soit conserver m_name pour la mise au point, et ajouter un champ ayant le nom correct par rapport à l'application.
      Le fait que chaque cellule contienne une chaîne avec le nom de la classe (par exemple 'url') n'est pas gênant car Delphi utilise un cache de chaînes.

3.1 - Voire aussi

Pour d'autres exemples de squelettes, voyez aussi:

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:
  • soit une valeur surtypée du pointeur associé à chaque chaîne
  • soit un pointeur vers un RECORD
Ces deux simplifications nous font économiser 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).


5 - Télécharger le source

Vous pouvez télécharger:

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.

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