Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 73

Modélisation des données et choix de structures

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons endossé le rôle d'architecte en analysant le cahier des charges de notre "Gestionnaire de Contacts Simplifié". Nous avons décomposé le problème en fonctionnalités claires (ajouter, lister, rechercher, supprimer). Nous avons ainsi dessiné le plan de notre application.

Aujourd'hui, nous continuons ce travail de conception en nous concentrant sur les "matériaux" de notre construction : les données. Avant d'écrire la moindre ligne de logique, nous devons décider de la manière la plus intelligente d'organiser les informations en mémoire. C'est ce qu'on appelle la modélisation des données. Votre objectif est d'apprendre à choisir les structures de données appropriées et, surtout, à justifier vos choix.

Cette étape est fondamentale pour atteindre votre but d'écrire du "code clean". Un bon choix de structure peut rendre un algorithme simple, rapide et élégant. Un mauvais choix peut le transformer en un code lent, complexe et difficile à maintenir.

1. Modéliser une donnée complexe : le cas d'un contact

Notre première tâche est de réfléchir à la représentation d'un seul contact. Le cahier des charges nous dit qu'un contact est défini par un nom, un prénom et un numéro de téléphone. Il ne s'agit pas d'une donnée simple comme un entier ou une chaîne de caractères, mais d'un ensemble d'informations hétérogènes qui doivent rester groupées.

Pour cela, l'outil idéal est l'enregistrement (souvent appelé structure ou struct dans les langages de programmation). Un enregistrement est une structure de données qui permet de regrouper plusieurs variables de types différents sous un seul nom.

Pour bien comprendre ce concept, je vous invite à étudier la section suivante d'un cours sur les structures de données.

[PDF] Algorithmique & Structures de données 1 Cours & Exercices corrigés

Le document 'Algorithmique & Structures de données 1' présente le concept d'enregistrement. Cette lecture vous montrera comment déclarer une telle structure pour regrouper des informations.

Dans le chapitre 6, 'Les Enregistrements', lisez les sections 'Introduction' (6.1) et 'Déclaration d’un enregistrement' (6.3). Concentrez-vous sur la syntaxe de déclaration et la manière d'accéder aux champs d'un enregistrement avec le point ('.'), comme expliqué dans la section 6.3.2.

En nous basant sur ce que vous venez de lire, nous pouvons modéliser notre contact en pseudo-code de la manière suivante :

TYPE Contact = ENREGISTREMENT
    nom: CHAINE
    prenom: CHAINE
    telephone: CHAINE
FIN ENREGISTREMENT

Désormais, Contact est un nouveau type que nous pouvons utiliser. Par exemple, si nous déclarons une variable nouveau_contact de type Contact, nous pourrons accéder à son nom avec nouveau_contact.nom.


2. Modéliser la collection : comment stocker l'annuaire ?

Maintenant que nous savons représenter un contact, nous devons choisir comment stocker tous les contacts. Nous avons besoin d'une structure capable de contenir une collection d'enregistrements de type Contact.

Les principaux candidats que nous avons étudiés jusqu'ici sont le tableau et la liste chaînée. Pour faire un choix éclairé, nous devons analyser les avantages et inconvénients de chaque structure par rapport aux fonctionnalités de notre projet :

  1. Ajouter un contact (après avoir vérifié qu'il n'existe pas).
  2. Lister tous les contacts.
  3. Rechercher un contact (par nom).
  4. Supprimer un contact (identifié par nom et prénom).

L'article suivant est une excellente ressource pour comparer les structures de données et comprendre les compromis en termes de performance.

Les 5 structures de données à connaître absolument - Rachid Saad

L'article 'Les 5 structures de données à connaître absolument' de Rachid Saad synthétise parfaitement les forces et faiblesses des structures les plus courantes. Il va nous être très utile pour justifier notre choix.

Veuillez lire les sections suivantes : 'Introduction' : pour vous rappeler pourquoi ce choix est crucial. 'Le Tableau' et 'La Liste chaînée' : pour rafraîchir vos connaissances sur leurs propriétés. 'Tableau comparatif' : C'est la section la plus importante. Étudiez attentivement les deux tableaux. Le premier vous aide à choisir une structure selon le besoin, et le second détaille les complexités (Big O) de chaque opération. C'est la base de notre justification.

Analyse comparative pour notre projet

Armés de ces connaissances, analysons nos besoins spécifiques :

Fonctionnalité Performance avec un Tableau Performance avec une Liste Chaînée
Ajouter un contact La recherche du duplicata coûte O(n). L'ajout à la fin est O(1). Total : O(n). La recherche du duplicata coûte O(n). L'ajout est O(1). Total : O(n).
Lister les contacts Il faut parcourir tous les éléments. Coût : O(n). Il faut parcourir tous les éléments. Coût : O(n).
Rechercher un contact Il faut parcourir le tableau. Coût : O(n). Il faut parcourir la liste. Coût : O(n).
Supprimer un contact La recherche coûte O(n). La suppression implique de décaler les éléments suivants, ce qui coûte aussi O(n). Total : O(n). La recherche coûte O(n). Une fois l'élément trouvé, la modification des pointeurs est O(1). Le coût total est dominé par la recherche : O(n).

Cette analyse nous révèle une information capitale : pour les fonctionnalités de notre projet, il n'y a pas de différence de performance significative (en termes de complexité Big O) entre un tableau et une liste chaînée. Dans les deux cas, l'opération la plus coûteuse est la recherche qui nécessite de parcourir la collection.


3. Faire et justifier le choix final

Puisque la performance ne nous permet pas de trancher, nous devons nous baser sur un autre critère : la simplicité de mise en œuvre.

  • Le tableau est généralement plus simple à manipuler pour un débutant. L'accès à un élément via son indice (annuaire[i]) est direct et intuitif.
  • La liste chaînée demande de manipuler des pointeurs (courant.suivant), ce qui ajoute une couche de complexité.

Décision : Pour le projet de gestionnaire de contacts, le tableau d'enregistrements est le choix le plus pragmatique.

Justification :

  1. Simplicité de manipulation : La logique de parcours avec un indice est plus simple à écrire et à tracer manuellement que la gestion de pointeurs.
  2. Performance équivalente : Comme démontré dans notre analyse, les opérations clés de notre cahier des charges ont une complexité en O(n) dans les deux cas, car elles sont toutes limitées par la nécessité de rechercher un élément. Il n'y a donc pas d'avantage de performance à utiliser une liste chaînée.
  3. Adéquation au problème : Un tableau est une structure naturelle pour représenter une liste d'éléments.

Le modèle de données complet

En combinant nos deux choix, voici le modèle de données complet pour notre projet en pseudo-code :

// Étape 1 : Définir la structure pour un seul contact
TYPE Contact = ENREGISTREMENT
    nom: CHAINE
    prenom: CHAINE
    telephone: CHAINE
FIN ENREGISTREMENT

// Étape 2 : Choisir une structure pour la collection et la déclarer
// Nous utilisons un tableau et gérons sa taille réelle.
CONSTANTE MAX_CONTACTS = 100 // Taille maximale allouée

VARIABLES
    annuaire: TABLEAU[1..MAX_CONTACTS] DE Contact
    nb_contacts_actuels: ENTIER // Compteur pour savoir combien de contacts sont réellement dans le tableau

Au début du programme, nb_contacts_actuels sera initialisé à 0. Chaque fois que nous ajouterons un contact, nous l'insérerons à l'indice nb_contacts_actuels + 1 et nous incrémenterons ce compteur.


4. Exercice pratique

Pour vous assurer que vous avez bien compris la démarche, mettons-nous en situation avec un nouveau problème.

Cahier des charges : Gestion d'une file d'attente pour un service client

Le programme doit simuler une file d'attente. Les clients arrivent et sont ajoutés à la fin de la file. Lorsqu'un conseiller est libre, il prend en charge le prochain client, c'est-à-dire celui qui est en tête de file depuis le plus longtemps. Un client est représenté par son nom et le motif de son appel.
Les fonctionnalités sont :

  1. Ajouter_Client_En_Attente (le nouveau client va à la fin de la file).
  2. Traiter_Client_Suivant (le client en tête de file est retiré).
  3. Afficher_Prochain_Client (sans le retirer de la file).

Votre mission :

  1. Proposez une modélisation en pseudo-code pour un Client en utilisant un ENREGISTREMENT.
  2. Quelle structure de données choisiriez-vous pour gérer la file d'attente (Tableau, Liste Chaînée, Pile ou File) ?
  3. Rédigez une courte justification de votre choix en expliquant pourquoi la structure choisie est la plus appropriée et pourquoi les autres ne le sont pas, en vous basant sur les principes LIFO (Last-In, First-Out) et FIFO (First-In, First-Out).
Cliquez ici pour voir une proposition de solution
  1. Modélisation d'un Client :

    TYPE Client = ENREGISTREMENT
        nom: CHAINE
        motif_appel: CHAINE
    FIN ENREGISTREMENT
    
  2. Choix de la structure : La File (Queue).

  3. Justification : Le cahier des charges décrit un comportement "premier arrivé, premier servi", ce qui correspond exactement au principe FIFO (First-In, First-Out).

    • Une File est la structure de données conçue spécifiquement pour ce besoin. L'opération Ajouter_Client_En_Attente correspond à enfiler (ajouter à l'arrière) et Traiter_Client_Suivant correspond à défiler (retirer par l'avant).
    • Une Pile serait incorrecte car elle suit le principe LIFO (Dernier entré, premier servi). Le dernier client arrivé serait le premier traité, ce qui est injuste.
    • Un Tableau ou une Liste Chaînée pourraient être utilisés pour implémenter une file, mais la structure "File" représente plus directement et plus clairement l'intention du problème. Utiliser une "File" rend l'algorithme plus lisible et aligné avec le métier, ce qui est un des piliers du "code clean".

Conclusion

Aujourd'hui, vous avez appris à transformer les besoins d'un projet en un modèle de données concret et justifié. Cette compétence est au cœur du métier de développeur.

Points clés à retenir :

  • On modélise les données en deux temps : d'abord l'entité individuelle (souvent avec un enregistrement), puis la collection qui les regroupe.
  • Le choix d'une structure de collection (Tableau, Liste Chaînée, File, Pile...) doit être justifié en analysant les opérations requises par le projet.
  • La complexité algorithmique (Big O) est l'outil principal pour comparer les performances, mais si elles sont équivalentes, des critères comme la simplicité et la lisibilité deviennent décisifs.

Nous avons maintenant un plan des fonctionnalités et une structure pour nos données. La prochaine étape logique est de connecter les deux. Dans la leçon suivante, nous allons concevoir l'architecture modulaire du programme, en définissant les fonctions précises qui manipuleront le modèle de données que nous venons d'établir.