|
u_c_tstringlist - John COLIBRI.
|
- mots clé:template - modèle - encapsulation de tStringList
- logiciel utilisé: Windows 98, Delphi 5.0
- matériel utilisé: Pentium 500Mhz, 128 M de mémoire
- champ d'application: Delphi 1 à 6 sur Windows, Kylix
- niveau: débutant en Pascal et Delphi
- uses: u_c_basic_object, u_c_display, (u_c_log,
u_loaded, u_strings, u_types_constants, u_c_basic_file,
u_c_file_name, u_dir)
- plan:
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_name: String; p_c_path_list: c_path_list;
p_file: String);
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_max, Length(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_file: Integer;
l_path, l_previous_path: String;
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_file) do
begin
l_path:= m_name;
if l_path= l_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_list= Class; // forward
c_xxx= // one "xxx"
Class(c_basic_object)
public
// -- not the same name as the tStringList
m_c_parent_xxx_list: c_xxx_list;
m_yyy: String;
Constructor create_xxx(p_name: String; p_c_xxx_list: c_xxx_list;
p_yyy: String); Virtual;
function f_display_xxx: String;
Destructor Destroy; Override;
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_list: tStringList;
Constructor create_xxx_list(p_name: String); Virtual;
function f_xxx_count: Integer;
function f_c_xxx(p_xxx: Integer): c_xxx;
function f_index_of(p_xxx: String): Integer;
function f_c_find_by_xxx(p_xxx: String): c_xxx;
procedure add_xxx(p_xxx: String; p_c_xxx: c_xxx);
procedure add_element(p_xxx, p_yyy: String);
procedure add_unique_xxx(p_xxx, p_yyy: String);
procedure display_xxx_list;
Destructor Destroy; Override;
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_count: Integer;
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_index: Integer): 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" :
- 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 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 la
développement de projets pour
ses clients, le conseil 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, Ado.Net, Asp.Net et UML qu'il
anime personellement tous les mois, à Paris, en province ou sur site client.
|