> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Principe de responsabilité unique : les niveaux d'abstraction
- URL: https://www.sfeir.dev/single-responsibility-principle-les-niveaux-dabstraction/
- Published: 2025-02-13T10:38:29.000Z
- Updated: 2025-02-13T10:38:29.000Z
- Description: Une fonction ne doit faire qu’une seule chose, mais comment savoir si c’est vraiment le cas ? À travers un exemple concret, découvrons comment appliquer le Single Responsibility Principle en respectant les niveaux d’abstraction pour un code plus clair et maintenable.
- Author: Pacifique Cishiku Mukuna
- Tags: SOLID, Clean-code, Uncle Bob, Algorithmes, bonnes pratiques, code, Java, programming

L’un des [principes SOLID](https://www.sfeir.dev/les-principes-solid-pourquoi-faire/) les plus importants est le **Single Responsibility Principle (SRP)**, ou **Principe de Responsabilité Unique** en français.D’après [**Robert C. Martin**](https://www.sfeir.dev/back/robert-martin-aka-uncle-bob-le-pere-de-la-clean-architecture/) **(Uncle Bob)** dans son livre *Clean Code*, une fonction ne doit avoir qu’une seule responsabilité.En lisant ce livre, je suis tombé sur un cas où l’auteur appliquait des techniques de refactorisation à un code existant, afin de rendre une fonction aussi simple et claire que possible.Pour lui, après refactorisation, cette fonction ne faisait plus qu’une seule chose. Voici la fonction :

```java
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) {
    if (isTestPage(pageData)){
        includeSetupAndTeardownPages(pageData, isSuite);
    }
    return pageData.getHtml();
}

```

### Analysons la fonction

En examinant le code, on peut voir que cette fonction exécute en réalité **trois actions distinctes** :

- Vérifie si la page est une page de test ;
- Si c’est le cas, elle inclut les *setups* et *teardowns* ;
- Retourne le HTML de la page.

#### Les niveaux d’abstraction

À partir de là, l’auteur introduit la notion de **niveaux d’abstraction** dans le Single Responsibility Principle (SRP). Il affirme que :

> “Une fonction ne fait qu’une seule chose si toutes les instructions qu’elle contient sont au même niveau d’abstraction.”

Je dois avouer que, même après avoir relu cette phrase plusieurs fois, je n’arrivais pas à comprendre comment cette fonction ne faisait qu’une seule chose… Jusqu’à ce que je décide de **mettre la main à la pâte**. Et là, tout a pris sens !Prenons un exemple fictif pour mieux comprendre ce principe.

### Exemple : traitement d’une approbation d’élève

Imaginons que dans un système de gestion d'une école, nous avons une fonction qui est responsable du **traitement de l’approbation des élèves**. Ce traitement consiste à **mettre à jour les informations de l’élève et à envoyer un e-mail de notification**.Voici à quoi ressemble la fonction initiale:

```java
public void traiterApprobation(EtudiantDTO dto) {
    Etudiant etudiant = repository.findBy(dto.getMatricule());
    etudiant.setNote(dto.getNote());
    etudiant.setApprouve(true);
    repository.save(etudiant);

    String contenu = new StringBuilder()
            .append("Bonjour !\n\n Nous vous informons que l'étudiant ")
            .append(dto.getNomComplet())
            .append(" a réussi avec la note de ")
            .append(dto.getNote())
            .toString();

    Email email = new Email.EmailBuilder()
            .destinataires(singletonList(dto.getEmailDuResponsable()))
            .destinatairesEnCopie(singletonList(dto.getEmail()))
            .contenu(contenu)
            .build();

    mailSender.send(email);
}

```

Si on regarde bien, cette fonction exécute plusieurs actions différentes. Pour mieux les identifier, ajoutons des commentaires pour les séparer :

```java
public void traiterApprobation(EtudiantDTO dto) {
    //Actualiser les données de l'étudiant
    Etudiant etudiant = repository.findBy(dto.getMatricule());
    etudiant.setNote(dto.getNote());
    etudiant.setApprouve(true);
    //Sauvegarder à la dataBase
    repository.save(etudiant);

    //Monter le corps du mail qui sera envoyé
    String contenu = new StringBuilder()
            .append("Bonjour !\n\n Nous vous informons que l'étudiant ")
            .append(dto.getNomComplet())
            .append(" a réussi avec la note de ")
            .append(dto.getNote())
            .toString();

    //Créer le mail avec les destinataires et le contenu
    Email email = new Email.EmailBuilder()
            .destinataires(singletonList(dto.getEmailDuResponsable()))
            .destinatairesEnCopie(singletonList(dto.getEmail()))
            .contenu(contenu)
            .build();

    //Envoyer le mail
    mailSender.send(email);
}

```

À ce stade, il est évident que notre fonction fait **plusieurs choses en même temps**. Mais la vraie question est : **perçois-tu les différents niveaux d’abstraction qu’elle contient ?** L’objectif de cette fonction est de **traiter l’approbation**.

> "Le traitement de l’approbation consiste à mettre à jour les données de l’élève et à envoyer un e-mail pour informer qu’il a été approuvé."

Donc, l’envoi d’un e-mail fait bien **partie des responsabilités de cette fonction**. Mais, par contre, décider si l’e-mail doit commencer par *"Bonjour"* ou *"Salut"* est un **détail d’implémentation** dont cette fonction ne devrait pas se préoccuper. Ce genre de décision est **à un niveau d’abstraction plus bas**.

### Séparation des niveaux d’abstraction

L’idée maintenant est de **séparer les différents niveaux d’abstraction** en créant des fonctions dédiées.En appliquant la technique de **l’extraction de méthodes**, nous pouvons commencer par déplacer la mise à jour des données de l’élève dans une fonction spécifique :

```java
private void mettreAJourDonneesEtudiant(final EtudiantDTO dto) {
    Etudiant etudiant = repository.findBy(dto.getMatricule());
    etudiant.setNote(dto.getNote());
    etudiant.setApprouve(true);
    repository.save(etudiant);
}

```

Ensuite, nous appliquons la même technique pour **l’envoi de l’e-mail**, en le séparant en plusieurs niveaux d’abstraction :

```java
private void envoyerMailApprobation(final EtudiantDTO dto) {
    mailSender.send(creerMailApprobation(dto));
}

private Email creerMailApprobation(final EtudiantDTO dto) {
    return new Email.EmailBuilder()
            .destinataires(singletonList(dto.getEmailDuResponsable()))
            .destinatairesEnCopie(singletonList(dto.getEmail()))
            .contenu(genererContenuMailApprobation(dto))
            .build();
}

private String genererContenuMailApprobation(final EtudiantDTO dto) {
    return new StringBuilder()
            .append("Bonjour !\n\n Nous vous informons que l'étudiant ")
            .append(dto.getNomComplet())
            .append(" a réussi avec la note de ")
            .append(dto.getNote())
            .toString();
}

```

Remarque : j’ai créé **une fonction pour assembler l’e-mail** (ajout des destinataires et du contenu) et **une autre fonction uniquement pour générer le contenu de l’e-mail**. Pourquoi ? Parce que ces deux tâches **sont à des niveaux d’abstraction différents**.

### Résultat final

Après ces modifications, voici à quoi ressemble notre fonction principale :

```java
public void traiterApprobation(final EtudiantDTO dto) {
    mettreAJourDonneesEtudiant(dto);
    envoyerMailApprobation(dto);
}

```

Si je te demande : **"Combien de choses fait cette fonction ?"**, tu pourrais répondre :

> "Deux choses : elle met à jour les données de l’élève et envoie un e-mail."

Si c’est ta réponse, **tu n’as ni totalement tort, ni totalement raison hahaha**. Reprenons la définition de notre fonction :

> "Le traitement de l’approbation consiste à mettre à jour les données de l’élève et à envoyer un e-mail de notification."

Autrement dit, **mettre à jour les données et envoyer un e-mail sont toutes, au même niveau d’abstraction**. On peut donc conclure que notre fonction **fait bien une seule chose** : **Elle traite l’approbation des élèves.**