Aller au contenu principal

Modularité

Ce que ce chapitre apporte

  • Comprendre la notion de module et son rôle dans la conception d'un algorithme.
  • Savoir organiser un programme en plusieurs fonctions cohérentes.
  • Identifier les avantages de la modularité : clarté, réutilisation et maintenance.
  • Être capable de concevoir une décomposition hiérarchique d'un problème

Un programme qui tient en une page se lit. Un programme qui tient en dix ne se lit plus, sauf s'il est découpé. La modularité consiste à répartir le travail entre des modules qui font chacun une seule chose, puis à écrire un programme principal qui se contente de les enchaîner. C'est la différence entre un tas d'instructions et un programme qu'on peut reprendre six mois plus tard.

Lorsqu'un programme devient plus long ou plus complexe, il devient difficile de le comprendre, de le corriger ou de le faire évoluer. Pour éviter cela, on apprend à le découper en plusieurs **modules** : des blocs logiques autonomes, chacun responsable d'une tâche bien précise. Ce principe s'appelle la **modularité**.

Pourquoi décomposer un programme ?

Définition

La décomposition consiste à séparer un problème complexe en sous-problèmes plus simples, appelés modules. Chaque module peut ensuite être développé, testé et corrigé indépendamment des autres.

Un programme complet peut être vu comme une machine composée de plusieurs engrenages :
chaque engrenage joue un rôle spécifique, et l'ensemble ne fonctionne que si chaque partie fait bien son travail.

Par exemple, pour un programme de gestion de notes :

  • un module s'occupe de la saisie des données,
  • un autre calcule la moyenne,
  • un troisième affiche les résultats.

Cette approche permet de raisonner par étapes et de simplifier la mise au point du programme.

Astuce

Avant de coder, prendre l'habitude de découper le problème en sous-tâches logiques. Une bonne question à se poser est : « Puis-je expliquer mon algorithme en trois ou quatre grandes étapes claires ? »

Qu'est-ce qu'un module ?

Définition

Un module est une partie autonome d'un programme qui remplit une fonction précise. Il peut être représenté par une fonction, une procédure, ou même un autre algorithme.

Chaque module peut être vu comme une brique indépendante du programme.
Ces briques peuvent ensuite être assemblées pour construire un système complet.

Exemple

Algorithme
pas 1 / 29
séparées par des virgules, décimales au point
Fonction LireNotes()
Pour i de 1 à 5
Lire n[i]
FinPour
Retourner n
FinFonction
Fonction CalculMoyenne(t)
somme 0
Pour i de 1 à 5
somme somme + t[i]
FinPour
Retourner somme / 5
FinFonction
Fonction AfficherResultat(moyenne)
Écrire "Moyenne : ", moyenne
FinFonction
Début
notes LireNotes()
m CalculMoyenne(notes)
AfficherResultat(m)
Fin

programme principal

aucune variable

LireNotes

i1

Ici, le programme principal se limite à enchaîner des modules : la lecture, le calcul et l'affichage. Chaque fonction fait une seule chose, ce qui rend l'algorithme lisible et facile à modifier.

Diagramme de séquence
PrincipalLireNotesCalculMoyenneAfficherResultatappel sans argumentle tableau des cinq notesle tableaula moyennela moyenneaucune flèche ne relie deux modules entre eux

Toutes les flèches partent du programme principal ou y reviennent. Aucun module n'en appelle un autre, et aucune donnée ne circule autrement que par un argument ou une valeur retournée. C'est cette forme en étoile qui rend chaque module utilisable ailleurs, tel quel.

Le tableau circule par les deux portes, jamais autrement
LireNotes construit le tableau et le retourne ; le programme principal le range dans notes et le passe à CalculMoyenne. À aucun moment les deux fonctions ne partagent une variable.

On pourrait croire que passer par une variable connue des deux serait plus simple, et on gagnerait en effet quelques caractères. On perdrait la propriété qui fait tout l'intérêt du découpage : CalculMoyenne se relit, se teste et se remplace sans savoir qui l'appelle ni d'où vient le tableau. Cette fonction moyenne cinq nombres, un point c'est tout.

Organisation d'un programme modulaire

Un programme bien structuré suit généralement une organisation logique en trois parties :

PartieRôle
DéclarationDéfinir les variables, constantes et fonctions utilisées.
Corps principalDécrire le déroulement global du programme.
ModulesDétailler les fonctions appelées dans le corps principal.

Cette structure facilite la compréhension du flux global du programme et évite les répétitions.

On peut comparer cela à un livre :

  • la table des matières (le corps principal) indique l'ordre,
  • les chapitres (les fonctions) développent chaque idée séparément.

Communication entre modules

Les modules échangent des données à l'aide de paramètres (entrées) et de valeurs de retour (sorties). C'est ce qui permet à chaque fonction de travailler indépendamment tout en participant à la résolution globale du problème.

Algorithme
Fonction Addition(a, b)
Retourner a + b
FinFonction
Fonction AfficheSomme()
Lire x, y
Écrire "Somme : ", Addition(x, y)
FinFonction

Le module Addition effectue le calcul, tandis que AfficheSomme s'occupe de la saisie et de l'affichage. Cette séparation des rôles rend le code plus clair et plus facile à tester.

Erreur fréquente

Confondre la variable locale d'un module avec une variable globale. Une variable déclarée à l'intérieur d'une fonction n'existe que dans cette fonction. Elle disparaît dès que la fonction se termine.

Vérification

Vérification rapideon peut se reprendre

1.Pourquoi découper un programme en modules ?

2.Qu'est-ce qui distingue un bon découpage d'un mauvais ?

3.Deux modules doivent échanger une information. Comment ?

4.Un module fonctionne seul mais échoue une fois assemblé. La cause la plus probable ?

5.Un même traitement apparaît à trois endroits. Que faire ?

Exercices type

Le but n'est pas d'écrire le programme le plus court, mais celui dont chaque morceau fait une seule chose et porte un nom qui le dit.

Exercice 1 : écrire un programme modulaire qui lit les notes de 5 élèves, calcule la moyenne, puis affiche « Réussi » ou « Échec ».

Afficher la solution

Trois responsabilités, donc trois modules, et un programme principal qui se contente de les enchaîner.

Algorithme
pas 1 / 37
séparées par des virgules, décimales au point
Fonction LireNotes()
Pour i de 1 à 5
Écrire "Note ", i, " : "
Lire notes[i]
FinPour
Retourner notes
FinFonction
Fonction CalculMoyenne(t)
somme 0
Pour i de 1 à 5
somme somme + t[i]
FinPour
Retourner somme / 5
FinFonction
Fonction AfficherResultat(moyenne)
Écrire "Moyenne : ", moyenne
Si moyenne >= 10 Alors
Écrire "Réussi"
Sinon
Écrire "Échec"
FinSi
FinFonction
Début
notes LireNotes()
moyenne CalculMoyenne(notes)
AfficherResultat(moyenne)
Fin

programme principal

aucune variable

LireNotes

i1

Relisons le Début : il se lit comme une phrase, sans avoir besoin de regarder le détail des fonctions. C'est exactement ce que la modularité achète.

Le test qui valide un découpage

Peut-on nommer chaque module par un verbe suivi d'un complément, sans « et » ? LireNotes, CalculMoyenne, AfficherResultat passent le test. Si on doit écrire LireEtCalculer, c'est que le module en fait deux et qu'il faut le couper.

Exercice 2 : écrire un programme modulaire qui lit deux nombres et affiche leur somme et leur produit, chaque opération étant effectuée dans une fonction distincte.

Afficher la solution
Algorithme
pas 1 / 8
séparées par des virgules, décimales au point
Fonction Somme(a, b)
Retourner a + b
FinFonction
Fonction Produit(a, b)
Retourner a * b
FinFonction
Début
Écrire "Premier nombre : "
Lire x
Écrire "Second nombre : "
Lire y
Écrire "Somme : ", Somme(x, y)
Écrire "Produit : ", Produit(x, y)
Fin

programme principal

aucune variable

Ce que Écrire a affiché

Premier nombre : 

Notons que Somme et Produit ne lisent rien et n'affichent rien : elles calculent, et c'est tout. Ce sont les fonctions les plus faciles à réutiliser, parce qu'elles ne dépendent de rien d'autre que de leurs paramètres.

Erreur fréquente

Mettre le Lire à l'intérieur de Somme. La fonction devient alors inutilisable ailleurs : impossible de calculer la somme de deux valeurs déjà connues sans les ressaisir. Une fonction qui calcule ne doit ni lire ni afficher.

La méthode

  1. Découper par tâche, pas par longueur. Un module fait une chose et une seule, et son nom doit suffire à la dire.
  2. Faire circuler les données par les paramètres et le retour, jamais par des variables partagées. C'est ce qui rend un module réutilisable ailleurs.
  3. Sortir les lectures et les affichages des modules de calcul. Un module qui lit au clavier ne peut plus servir sur des données venues d'ailleurs.
  4. Écrire d'abord le programme principal, comme une suite d'appels lisible, puis remplir les modules. La décomposition se juge à cette lecture.
  5. Vérifier chaque module isolément avant de les assembler. Une erreur trouvée dans un module de dix lignes coûte moins qu'une erreur cherchée dans cent.
  6. Se méfier d'un module qui a besoin de trop de paramètres : c'est le signe qu'il en fait plus qu'une chose.

Synthèse

  • Un module est une fonction qui réalise une tâche précise, et une seule.
  • Les modules communiquent par leurs paramètres et leur valeur de retour, pas par des variables partagées.
  • Le programme principal doit se lire comme un sommaire : lire, calculer, afficher.
  • Séparer ce qui calcule de ce qui lit et de ce qui affiche : c'est ce qui rend un module réutilisable.

Ces huit chapitres donnent de quoi écrire un algorithme correct. Ils ne disent pas encore comment en écrire un qui reste correct sur des données mille fois plus grandes, ni comment décrire un traitement par lui-même. La récursivité ouvre la seconde moitié du parcours sur une fonction qui s'appelle elle-même, et sur la pile d'appels qui la rend possible.

Mettre en pratique