Pourquoi factoriser : réduire la complexité sans perdre le contrôle
Quand un programme grandit, la difficulté vient rarement d’une instruction isolée, mais de la répétition et de l’enchevêtrement : mêmes vérifications recopiées à plusieurs endroits, mêmes calculs réécrits avec des variantes, mêmes transformations de données dispersées. Factoriser consiste à extraire ces morceaux récurrents dans des fonctions réutilisables. On obtient alors : (1) moins de copier-coller, (2) des règles centralisées, (3) des tests plus simples, (4) moins d’erreurs liées aux variables globales et aux incohérences.
Fonctions : définition, paramètres, retour
Définir une fonction
Une fonction est un bloc nommé qui encapsule une logique. On l’appelle ensuite en fournissant des paramètres (entrées). Elle peut produire une valeur de retour (sortie).
fonction nomFonction(param1, param2, ...): typeRetour optionnel
... instructions ...
retourner valeurParamètres : ce que la fonction reçoit
Les paramètres sont des variables locales à la fonction. Ils évitent de dépendre de variables globales et rendent la fonction réutilisable dans d’autres contextes.
- Bon réflexe : passer explicitement ce dont la fonction a besoin.
- À éviter : lire/écrire des variables globales “par commodité”.
Valeur de retour : ce que la fonction produit
Une fonction “pure” renvoie un résultat sans modifier le monde extérieur. C’est plus simple à tester : mêmes entrées → même sortie.
fonction aireRectangle(longueur, largeur): nombre
retourner longueur * largeurEffets de bord : ce que la fonction modifie en dehors d’elle
Un effet de bord apparaît quand une fonction modifie quelque chose qui n’est pas dans ses paramètres locaux : variable globale, fichier, base de données, interface, etc. Les effets de bord ne sont pas “interdits”, mais ils doivent être maîtrisés, car ils compliquent le débogage et les tests.
Continuez dans notre application.
Vous pouvez écouter le livre audio écran éteint, recevoir un certificat gratuit pour ce cours et accéder également à 5 000 autres cours en ligne gratuits.
Ou poursuivez votre lecture ci-dessous...Téléchargez l'application
| Type de fonction | Caractéristique | Conséquence |
|---|---|---|
| Pure | Retourne une valeur, ne modifie rien d’externe | Test facile, réutilisation forte |
| Avec effets de bord | Modifie un état externe (global, fichier, etc.) | Test plus difficile, dépendances implicites |
Astuce pratique : séparer “calculer” (pur) et “appliquer/afficher/enregistrer” (effets de bord) dans deux fonctions différentes.
Contrats : préconditions et postconditions
Préconditions
Une précondition est ce qui doit être vrai avant d’appeler la fonction. Exemple : “le prix doit être ≥ 0”, “la chaîne ne doit pas être vide”.
Postconditions
Une postcondition décrit ce que la fonction garantit après exécution si les préconditions sont respectées. Exemple : “le résultat est un nombre ≥ 0”, “la chaîne retournée est en majuscules et sans espaces en bord”.
fonction estDansIntervalle(x, min, max): booléen
# Préconditions : min ≤ max
# Postcondition : retourne vrai ssi min ≤ x ≤ max
si min > max:
retourner faux # ou signaler une erreur selon votre convention
retourner (x ≥ min) ET (x ≤ max)Découper un problème en sous-problèmes testables
Factoriser n’est pas “couper au hasard”. L’objectif est de créer des sous-problèmes qui se testent indépendamment. Une méthode simple :
- Étape 1 — Repérer les répétitions : mêmes calculs, mêmes validations, mêmes transformations.
- Étape 2 — Nommer l’intention : le nom de la fonction doit dire “ce que ça fait”, pas “comment”.
- Étape 3 — Définir l’interface : paramètres minimaux, retour clair.
- Étape 4 — Écrire le contrat : préconditions/postconditions en commentaires.
- Étape 5 — Tester la fonction seule : quelques cas typiques + cas limites.
- Étape 6 — Remplacer le copier-coller : utiliser la fonction partout.
Pseudo-code réutilisable : conventions recommandées
- Utiliser des noms explicites :
normaliserChaine,calculerTaxe,validerEmailSimple. - Éviter les dépendances cachées : pas de lecture/écriture de variables globales si possible.
- Retourner des valeurs plutôt que modifier des paramètres (sauf si c’est l’objectif).
- Documenter les règles métier dans le contrat.
Exercice 21 — Fonction de validation d’email simplifiée (règles minimales)
Énoncé
Écrire une validation d’email “minimale” avec ces règles : (1) contient exactement un @, (2) il y a au moins 1 caractère avant @, (3) il y a au moins 1 caractère après @, (4) la partie après @ contient au moins un point . qui n’est ni au début ni à la fin de cette partie. On ne cherche pas à couvrir toutes les règles réelles des emails.
Solution monolithique (à éviter)
fonction validerEmailSimple_monolithique(email): booléen
# Mélange de règles + détails d’implémentation
si email est null:
retourner faux
# Compter les '@'
compteurArobase = 0
pour chaque caractere c dans email:
si c == '@':
compteurArobase = compteurArobase + 1
si compteurArobase != 1:
retourner faux
positionArobase = positionDe('@', email)
si positionArobase == 0:
retourner faux
si positionArobase == longueur(email) - 1:
retourner faux
domaine = sousChaine(email, positionArobase + 1, longueur(email))
# Vérifier qu'il y a un '.' pas en bord
positionPoint = positionDe('.', domaine)
si positionPoint == -1:
retourner faux
si positionPoint == 0:
retourner faux
si positionPoint == longueur(domaine) - 1:
retourner faux
retourner vraiProblèmes : logique longue, difficile à relire, risque de copier-coller si on refait une validation similaire ailleurs, et mélange de sous-tâches (compter, découper, vérifier).
Solution factorisée (recommandée)
fonction contientExactementUnArobase(texte): booléen
# Précondition : texte non null
# Postcondition : vrai ssi texte contient exactement un '@'
compteur = 0
pour chaque c dans texte:
si c == '@':
compteur = compteur + 1
retourner compteur == 1
fonction domaineValide(domaine): booléen
# Précondition : domaine non vide
# Postcondition : vrai ssi domaine contient un '.' non placé en bord
pos = positionDe('.', domaine)
si pos == -1:
retourner faux
retourner (pos > 0) ET (pos < longueur(domaine) - 1)
fonction validerEmailSimple(email): booléen
# Préconditions : email non null
# Postcondition : vrai ssi règles minimales respectées
si email est null:
retourner faux
si NON contientExactementUnArobase(email):
retourner faux
posA = positionDe('@', email)
si posA == 0 OU posA == longueur(email) - 1:
retourner faux
domaine = sousChaine(email, posA + 1, longueur(email))
si NON domaineValide(domaine):
retourner faux
retourner vraiCommentaires : la lecture devient “narrative” (une règle par ligne). Les erreurs évitées : si la règle “exactement un @” change, on modifie une seule fonction. On limite aussi les variables temporaires au strict nécessaire et on évite de dépendre d’un état global.
Exercice 22 — Fonction estDansIntervalle réutilisée dans plusieurs contrôles
Énoncé
Créer une fonction estDansIntervalle(x, min, max) et l’utiliser pour valider : (1) un âge entre 0 et 120, (2) une note entre 0 et 20, (3) une température acceptable entre 18 et 26.
Solution monolithique (copier-coller)
fonction controles_monolithiques(age, note, temperature): booléen
ok = vrai
# âge
si age < 0 OU age > 120:
ok = faux
# note
si note < 0 OU note > 20:
ok = faux
# température
si temperature < 18 OU temperature > 26:
ok = faux
retourner okProblèmes : répétition, risque d’incohérence (un jour on met 119 au lieu de 120), et si on change la règle “inclusif/exclusif”, il faut corriger partout.
Solution factorisée (réutilisation)
fonction estDansIntervalle(x, min, max): booléen
# Préconditions : min ≤ max
# Postcondition : vrai ssi min ≤ x ≤ max
si min > max:
retourner faux
retourner (x ≥ min) ET (x ≤ max)
fonction controles_factorises(age, note, temperature): booléen
okAge = estDansIntervalle(age, 0, 120)
okNote = estDansIntervalle(note, 0, 20)
okTemp = estDansIntervalle(temperature, 18, 26)
retourner okAge ET okNote ET okTempCommentaires : la fonction centralise la définition de “dans l’intervalle”. Erreurs évitées : copier-coller et variations involontaires. Lisibilité : on voit immédiatement les bornes métier.
Exercice 23 — Calculer un prix final via fonctions (remise, taxe, frais)
Énoncé
On veut calculer un prix final à partir d’un prix de base : appliquer une remise (pourcentage), puis ajouter une taxe (pourcentage), puis ajouter des frais fixes. Écrire une version monolithique puis une version factorisée avec des fonctions : appliquerRemise, calculerTaxe, calculerPrixFinal. Règles : prixBase ≥ 0, remise entre 0 et 100, taxe ≥ 0, frais ≥ 0.
Solution monolithique
fonction prixFinal_monolithique(prixBase, remisePct, taxePct, fraisFixes): nombre
si prixBase < 0:
retourner -1
si remisePct < 0 OU remisePct > 100:
retourner -1
si taxePct < 0:
retourner -1
si fraisFixes < 0:
retourner -1
prixApresRemise = prixBase - (prixBase * remisePct / 100)
taxe = prixApresRemise * taxePct / 100
total = prixApresRemise + taxe + fraisFixes
retourner totalProblèmes : la validation et le calcul sont mélangés, et si on a besoin de “prix après remise” ailleurs, on recopie la formule (risque d’erreur d’arrondi, de pourcentage, etc.).
Solution factorisée
fonction appliquerRemise(prix, remisePct): nombre
# Préconditions : prix ≥ 0, 0 ≤ remisePct ≤ 100
# Postcondition : retourne un prix ≥ 0
retourner prix - (prix * remisePct / 100)
fonction calculerTaxe(montant, taxePct): nombre
# Préconditions : montant ≥ 0, taxePct ≥ 0
# Postcondition : retourne une taxe ≥ 0
retourner montant * taxePct / 100
fonction calculerPrixFinal(prixBase, remisePct, taxePct, fraisFixes): nombre
# Préconditions : prixBase ≥ 0, 0 ≤ remisePct ≤ 100, taxePct ≥ 0, fraisFixes ≥ 0
# Postcondition : retourne le total ≥ 0
si prixBase < 0 OU remisePct < 0 OU remisePct > 100 OU taxePct < 0 OU fraisFixes < 0:
retourner -1
apresRemise = appliquerRemise(prixBase, remisePct)
taxe = calculerTaxe(apresRemise, taxePct)
retourner apresRemise + taxe + fraisFixesCommentaires : chaque formule est isolée et réutilisable. Erreurs évitées : si la règle de remise change (ex. plafonnement), on modifie appliquerRemise une seule fois. On évite aussi les variables globales du type taxeCourante qui “traînent” entre calculs.
Exercice 24 — Normaliser une chaîne (trim/majuscule) avant comparaison
Énoncé
Avant de comparer des saisies utilisateur, normaliser les chaînes : supprimer les espaces au début/à la fin (trim) et convertir en majuscules. Objectif : comparer “ Paris ” et “PARIS” comme identiques. Écrire une version monolithique puis une version factorisée avec une fonction normaliserChaine.
Solution monolithique
fonction comparerVilles_monolithique(saisie1, saisie2): booléen
si saisie1 est null OU saisie2 est null:
retourner faux
a = enMajuscules(supprimerEspacesBords(saisie1))
b = enMajuscules(supprimerEspacesBords(saisie2))
retourner a == bProblèmes : ici c’est court, mais dès qu’on compare 5 champs (ville, pays, code, etc.), on recopie la normalisation partout. Et si on ajoute une règle (ex. remplacer les doubles espaces), il faut tout retrouver.
Solution factorisée
fonction normaliserChaine(texte): chaine
# Précondition : texte non null
# Postcondition : retourne une chaîne trim + majuscules
retour = supprimerEspacesBords(texte)
retour = enMajuscules(retour)
retourner retour
fonction comparerNormalise(a, b): booléen
# Préconditions : a et b non null
# Postcondition : vrai ssi normalisations identiques
retourner normaliserChaine(a) == normaliserChaine(b)Commentaires : la comparaison devient un appel clair. Erreurs évitées : oublier un trim dans un endroit, ou appliquer la majuscule sur une variable différente. On réduit aussi la tentation d’utiliser une variable globale du type dernierTexteNormalise qui peut être écrasée par un autre traitement.
Mini-guide : choisir de bonnes frontières de fonctions
Signes qu’un bloc doit devenir une fonction
- Vous copiez-collez le même code (même avec de petites variations).
- Vous avez besoin d’un nom pour résumer l’intention (“valider”, “normaliser”, “calculer”).
- Le bloc a des entrées/sorties claires et peut être testé seul.
- Le bloc mélange plusieurs responsabilités (ex. valider + calculer + afficher).
Checklist avant d’extraire
- Quels paramètres minimaux sont nécessaires ?
- Quel est le type de retour ? (booléen, nombre, chaîne, liste…)
- Y a-t-il des effets de bord ? Si oui, peut-on les isoler ?
- Quelles préconditions/postconditions écrire en 1–2 lignes ?
- Quels cas limites tester ? (vide, bornes, null, valeurs extrêmes)