Chargement...

Principes de Clean Code avec C#

Apprenez les principes de Clean Code avec C# pour écrire un code lisible, maintenable et évolutif.

Clean Code (Code Propre) est une philosophie de développement qui vise à écrire du code non seulement fonctionnel, mais aussi lisible, maintenable et extensible. Selon le principe « Le code est écrit une fois, mais lu mille fois. », l’objectif du code propre n’est pas simplement de produire un programme sans erreurs, mais de permettre à toute personne (y compris soi-même) de le comprendre facilement à l’avenir. Cet article présente les principes fondamentaux et des exemples pratiques pour écrire du code propre en C#.


1. Nommage Significatif (Meaningful Naming)

Les noms des variables, des méthodes et des classes influencent directement la lisibilité du code. Utilisez des noms courts mais explicites et orientés vers l’objectif, plutôt que des abréviations obscures.


// Mauvais exemple
int x = 5;
var lst = new List<string>();
void DoIt() { ... }

// Bon exemple
int retryCount = 5;
List<string> customerNames = new();
void ProcessOrder() { ... }

2. Principe de Responsabilité Unique (Single Responsibility Principle)

Chaque classe ou méthode doit avoir une seule responsabilité. Si une méthode écrit dans la base de données et envoie un e-mail, elle effectue probablement deux tâches distinctes.


// Mauvais exemple : plusieurs responsabilités
public class OrderService
{
    public void CompleteOrder(Order order)
    {
        SaveToDatabase(order);
        SendEmail(order);
    }
}

// Bon exemple : responsabilités séparées
public class OrderRepository
{
    public void Save(Order order) { ... }
}

public class EmailService
{
    public void SendOrderConfirmation(Order order) { ... }
}

Chaque classe ne devrait changer que pour une seule raison. Cela améliore la maintenabilité et facilite les tests.


3. Méthodes Courtes et Focalisées

Les longues méthodes augmentent la complexité et le risque d’erreurs. Chaque méthode doit être une petite unité cohérente qui effectue une seule tâche.


// Mauvais exemple
public void Process()
{
    // Plus de 100 lignes de logique...
}

// Bon exemple
public void Process()
{
    Validate();
    CalculateTotal();
    SaveChanges();
    NotifyCustomer();
}

Des méthodes petites et explicites sont plus faciles à tester, à réutiliser et à déboguer.


4. Évitez la Répétition (DRY – Don’t Repeat Yourself)

Répéter le même code rend la maintenance difficile et augmente les risques d’erreurs. Déplacez la logique commune dans des méthodes, classes utilitaires ou extensions.


// Mauvais exemple
if (age >= 18) Console.WriteLine("Majeur");
if (age >= 18) SendMail();

// Bon exemple
bool IsAdult(int age) => age >= 18;
if (IsAdult(age)) { Console.WriteLine("Majeur"); SendMail(); }

Moins il y a de répétition, plus les changements sont localisés et moins le code est sujet aux erreurs.


5. Réduisez les Commentaires — Le Code Doit Être Auto-Explicatif

Trop de commentaires deviennent rapidement obsolètes. Le code devrait idéalement expliquer ce qu’il fait par lui-même.


// Mauvais exemple
// Affiche le nom de l’utilisateur
Console.WriteLine(user.Name);

// Bon exemple
Console.WriteLine(user.FullName);

Les commentaires doivent expliquer pourquoi quelque chose est fait, pas ce qui est fait.


6. Utilisez des Constantes au Lieu de « Magic Numbers »

L’utilisation directe de nombres ou de chaînes (« magic numbers ») réduit la lisibilité. Définissez-les comme constantes ou variables nommées.


// Mauvais exemple
if (speed > 120) Console.WriteLine("Excès de vitesse!");

// Bon exemple
const int MaxSpeed = 120;
if (speed > MaxSpeed) Console.WriteLine("Excès de vitesse!");

7. Utilisez des Clauses de Garde (Guard Clauses)

Au lieu d’utiliser des blocs if imbriqués, faites des retours anticipés pour améliorer la lisibilité.


// Mauvais exemple
if (user != null)
{
    if (user.IsActive)
    {
        Process(user);
    }
}

// Bon exemple
if (user is null) return;
if (!user.IsActive) return;
Process(user);

Les guard clauses simplifient le flux du code en appliquant le principe du « retour anticipé ».


8. Injection de Dépendances (Dependency Injection)

Évitez de créer des objets directement dans votre code. Recevez plutôt les dépendances depuis l’extérieur ; cela améliore la testabilité et la flexibilité.


// Mauvais exemple
public class NotificationService
{
    private EmailSender sender = new EmailSender();
    public void Notify() => sender.Send();
}

// Bon exemple
public class NotificationService
{
    private readonly IEmailSender _sender;
    public NotificationService(IEmailSender sender) => _sender = sender;
    public void Notify() => _sender.Send();
}

Cette approche constitue la base des principes Inversion of Control (IoC) et Dependency Injection (DI).


9. Gestion des Exceptions

Ne capturez pas les exceptions si ce n’est pas nécessaire ; si vous le faites, traitez-les de manière pertinente.


// Mauvais exemple
try
{
    Save();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

// Bon exemple
try
{
    Save();
}
catch (SqlException ex)
{
    _logger.LogError(ex, "Une erreur de base de données s’est produite.");
    throw; // relance vers le niveau supérieur
}

Les blocs catch (Exception) trop larges rendent la gestion des erreurs difficile et masquent souvent des problèmes.


10. Respecter les Principes SOLID

Le code propre est conforme aux principes SOLID :

Ces principes constituent la base d’un code propre et maintenable.


11. Évitez la Complexité Inutile (KISS & YAGNI)


// Mauvais exemple
public interface IAnimal { void Bark(); void Fly(); void Swim(); }

// Bon exemple
public interface IDog { void Bark(); }
public interface IFish { void Swim(); }

Le code doit être simple, ciblé et non sur-généralisé.


12. Formatage et Cohérence


// Exemple .editorconfig
[*.cs]
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true

Exemple : Nettoyage d’un Service de Commande

Dans l’exemple ci-dessous, un code complexe et difficile à maintenir a été refactorisé selon les principes du code propre.


// Mauvais exemple
public class OrderManager
{
    public void Process(Order order)
    {
        if (order.Total <= 0) throw new Exception("Montant invalide");
        Console.WriteLine("Commande traitée : " + order.Id);
        File.AppendAllText("log.txt", DateTime.Now + " - " + order.Id);
    }
}

// Bon exemple
public interface ILogger { void Log(string msg); }
public class FileLogger : ILogger
{
    public void Log(string msg) => File.AppendAllText("log.txt", $"{DateTime.Now} - {msg}\n");
}

public class OrderValidator
{
    public void Validate(Order order)
    {
        if (order.Total <= 0) throw new ArgumentException("Le montant doit être supérieur à zéro");
    }
}

public class OrderService
{
    private readonly ILogger _logger;
    private readonly OrderValidator _validator;

    public OrderService(ILogger logger, OrderValidator validator)
    {
        _logger = logger;
        _validator = validator;
    }

    public void Process(Order order)
    {
        _validator.Validate(order);
        Console.WriteLine($"Commande traitée : {order.Id}");
        _logger.Log($"Commande {order.Id} traitée.");
    }
}

Chaque classe a désormais une responsabilité claire — le code est testable, lisible et maintenable.


TL;DR

  • Le code propre signifie lisibilité et maintenabilité.
  • Des noms explicites, des méthodes courtes, une seule responsabilité et pas de duplications sont essentiels.
  • Préférez une bonne nomination, des guard clauses et l’injection de dépendances plutôt que des commentaires excessifs.
  • Réduisez la complexité (KISS, YAGNI) et appliquez les principes SOLID.
  • Écrivez du code à la fois pour les humains et pour les machines.

Articles connexes