Bonjour ! Dans la leçon précédente, nous avons défini le modèle de données pour notre projet de "Gestionnaire de Contacts Simplifié". Nous avons choisi de représenter un contact avec un ENREGISTREMENT et de stocker l'annuaire dans un TABLEAU. Nous avons donc les fondations et les matériaux.
Aujourd'hui, nous allons dessiner le plan d'assemblage : l'architecture modulaire du programme. L'objectif est de décomposer notre application en un ensemble de fonctions indépendantes et cohérentes, et de définir comment elles vont collaborer. C'est une étape clé pour transformer un problème complexe en une série de tâches simples et gérables, ce qui est l'essence même de l'écriture de "code clean" et maintenable.
À la fin de cette leçon, vous saurez comment concevoir la structure d'un programme en définissant les fonctions nécessaires et la manière dont elles interagissent.
1. Qu'est-ce que l'Architecture Modulaire ?
Imaginez construire une voiture. Plutôt que de fabriquer une seule pièce massive, on conçoit des modules indépendants : le moteur, le système de freinage, la transmission, etc. Chaque module a une fonction précise et peut être développé, testé, et même remplacé indépendamment des autres. Ils communiquent via des interfaces bien définies (des câbles, des axes, etc.).
En algorithmique, c'est le même principe. Au lieu d'écrire un long bloc de code séquentiel, nous le décomposons en modules, qui sont nos fonctions et procédures.
Pour bien comprendre la philosophie derrière cette approche, je vous invite à lire une courte introduction sur le sujet.
Chapitre 7 — La modularité et la décomposition d'un programme
Le document 'Chapitre 7 — La modularité' explique pourquoi il est bénéfique de décomposer un programme. L'analogie avec les engrenages d'une machine est particulièrement parlante.
Lisez les deux premières sections : 'Pourquoi décomposer un programme ?' et 'Qu’est-ce qu’un module ?'. Concentrez-vous sur l'idée qu'un module est une 'brique indépendante' qui accomplit une tâche unique.
2. Principes d'une Bonne Architecture Modulaire
Décomposer un programme, c'est bien. Mais le faire intelligemment, c'est mieux. Une bonne architecture modulaire suit deux principes fondamentaux qui sont essentiels pour écrire du code de qualité.
Pour découvrir ces principes, je vous propose de consulter une présentation sur l'analyse modulaire.
cours1-algo-chapitre3-fonctions-procedures.pptx - Slideshare
Cette présentation, 'cours1-algo-chapitre3-fonctions-procedures.pptx', introduit les concepts théoriques qui guident une bonne décomposition modulaire. Elle vous aidera à comprendre ce qui différencie une architecture 'propre' d'une architecture 'désordonnée'.
Consultez la diapositive intitulée 'II. Principes de l'analyse modulaire' (slide 6). Focalisez-vous sur les définitions de 'Faible couplage' et 'Abstraction' / 'Encapsulation'. L'image comparant une bonne et une mauvaise décomposition est très instructive.
Retenons ces deux idées maîtresses :
-
Haute Cohésion : Chaque module doit avoir une seule et unique responsabilité. Une fonction nommée
CalculerMoyenneEtAfficherResultata une faible cohésion. Il vaut mieux la diviser en deux fonctions :CalculerMoyenneetAfficherResultat. C'est le principe de responsabilité unique. -
Faible Couplage : Les modules doivent être aussi indépendants que possible les uns des autres. Ils communiquent uniquement via leurs paramètres et valeurs de retour, sans dépendre de variables globales ou de détails internes d'autres modules. Un faible couplage permet de modifier un module sans risquer de "casser" tout le reste du programme.
3. Application au Projet : Le Gestionnaire de Contacts
Appliquons ces principes à notre projet. Nous allons définir la liste des fonctions (notre architecture) en nous basant sur les fonctionnalités identifiées dans la première leçon.
Pour chaque fonction, nous allons définir sa signature :
- Nom : Verbe d'action clair (ex:
AjouterContact). - Rôle : Une phrase décrivant sa responsabilité unique.
- Paramètres : Les données dont elle a besoin pour travailler (entrées) et celles qu'elle doit modifier (entrées/sorties).
- Valeur de retour : Ce qu'elle produit (sortie).
Le Programme Principal et les Fonctions Utilitaires
Le cœur de notre application sera une boucle qui affiche un menu et attend le choix de l'utilisateur. Cela nous suggère déjà une première fonction très simple et cohésive :
- Nom :
Procedure AfficherMenu() - Rôle : Afficher les options disponibles (Ajouter, Lister, Rechercher, Supprimer, Quitter).
- Paramètres : Aucun.
- Valeur de retour : Aucune.
Nous aurons aussi souvent besoin d'afficher les détails d'un seul contact. Créons une fonction "utilitaire" pour cela :
- Nom :
Procedure AfficherUnContact(contact_a_afficher) - Rôle : Afficher le nom, prénom et téléphone d'un contact formaté de manière lisible.
- Paramètres :
contact_a_afficher(Type:Contact, Mode: Entrée). - Valeur de retour : Aucune.
Les Fonctions Métiers
Maintenant, définissons les fonctions pour chaque grande fonctionnalité.
-
Rechercher un contact : C'est une opération fondamentale qui sera probablement réutilisée (par ex. avant d'ajouter un contact pour éviter les doublons, ou avant de le supprimer).
- Nom :
Fonction RechercherContact(annuaire, nb_contacts, nom, prenom) - Rôle : Cherche un contact par son nom et prénom dans l'annuaire.
- Paramètres :
annuaire(Type:TABLEAU DE Contact, Mode: Entrée)nb_contacts(Type:ENTIER, Mode: Entrée)nom(Type:CHAINE, Mode: Entrée)prenom(Type:CHAINE, Mode: Entrée)
- Valeur de retour : L'indice du contact dans le tableau s'il est trouvé, sinon -1 (une valeur conventionnelle pour "non trouvé").
- Nom :
-
Lister les contacts :
- Nom :
Procedure ListerContacts(annuaire, nb_contacts) - Rôle : Parcourt l'annuaire et affiche chaque contact.
- Paramètres :
annuaire(Type:TABLEAU DE Contact, Mode: Entrée)nb_contacts(Type:ENTIER, Mode: Entrée)
- Valeur de retour : Aucune.
- Interaction : Cette procédure fera une boucle et appellera
AfficherUnContactpour chaque élément.
- Nom :
4. Exercice : Définissez le reste de l'architecture
Maintenant, à vous de jouer ! En suivant le modèle ci-dessus, définissez la signature et le rôle des fonctions manquantes pour les fonctionnalités "Ajouter" et "Supprimer".
Votre mission :
Définissez la signature complète pour :
Procedure AjouterContact(...)Procedure SupprimerContact(...)
Pensez aux questions suivantes :
- De quelles données ces fonctions ont-elles besoin ?
- Doivent-elles modifier des données comme l'annuaire ou le nombre de contacts ? (Pensez au mode Entrée/Sortie pour les paramètres).
- Vont-elles faire appel à d'autres fonctions que nous avons déjà définies (comme
RechercherContact) ?
Cliquez ici pour voir une proposition de solution
-
Ajouter un contact : Cette procédure doit modifier l'annuaire et son compteur.
- Nom :
Procedure AjouterContact(annuaire, nb_contacts_actuels) - Rôle : Demande à l'utilisateur de saisir nom, prénom, téléphone. Vérifie si un contact avec le même nom/prénom n'existe pas déjà. Si non, l'ajoute à l'annuaire et met à jour le nombre de contacts.
- Paramètres :
annuaire(Type:TABLEAU DE Contact, Mode: Entrée/Sortie) : Il doit être modifiable.nb_contacts_actuels(Type:ENTIER, Mode: Entrée/Sortie) : Le compteur doit être incrémenté.
- Valeur de retour : Aucune.
- Interaction : Cette procédure appellera
RechercherContactpour vérifier l'existence du contact avant de l'ajouter.
- Nom :
-
Supprimer un contact : Cette procédure modifie aussi l'annuaire.
- Nom :
Procedure SupprimerContact(annuaire, nb_contacts_actuels) - Rôle : Demande à l'utilisateur le nom/prénom du contact à supprimer. Le recherche. S'il existe, le supprime du tableau (en décalant les éléments suivants pour ne pas laisser de "trou") et décrémente le nombre de contacts.
- Paramètres :
annuaire(Type:TABLEAU DE Contact, Mode: Entrée/Sortie)nb_contacts_actuels(Type:ENTIER, Mode: Entrée/Sortie)
- Valeur de retour : Aucune.
- Interaction : Cette procédure appellera
RechercherContactpour trouver l'indice du contact à supprimer.
- Nom :
5. Schéma des Interactions
Voici une vue d'ensemble de l'architecture que nous venons de concevoir, montrant comment les modules s'appellent entre eux :
Programme Principal
│
├── Appelle AfficherMenu()
│
├── Appelle AjouterContact(annuaire, nb_contacts)
│ └── Appelle RechercherContact(annuaire, ...)
│
├── Appelle ListerContacts(annuaire, nb_contacts)
│ └── Appelle AfficherUnContact(contact) // (dans une boucle)
│
├── Appelle SupprimerContact(annuaire, nb_contacts)
│ └── Appelle RechercherContact(annuaire, ...)
│
└── ... (autres fonctions comme une recherche simple et affichage)
Cette structure hiérarchique est claire, chaque fonction a un rôle défini, et les interactions sont maîtrisées. C'est la marque d'une conception logicielle robuste.
Conclusion
Félicitations ! Vous venez de concevoir l'architecture complète de notre application. Ce n'est plus une simple liste de fonctionnalités, mais un plan de construction détaillé.
Les points clés à retenir sont :
- L'architecture modulaire consiste à décomposer un programme en fonctions (modules) indépendantes.
- Une bonne architecture vise la haute cohésion (chaque module fait une seule chose) et le faible couplage (les modules sont indépendants).
- Définir la signature d'une fonction (nom, rôle, paramètres, retour) est l'acte de conception fondamental.
- Les interactions entre modules se font via des appels de fonction, en passant des données par paramètres.
Nous avons maintenant le plan des fonctionnalités, le modèle de données, et l'architecture modulaire. La table est mise ! Dans la prochaine leçon, nous passerons enfin à l'écriture : nous allons implémenter en pseudo-code chacune des fonctions que nous venons de définir pour donner vie à notre gestionnaire de contacts.