Skip to content

Instantly share code, notes, and snippets.

@bizouarn
Last active September 29, 2023 17:16
Show Gist options
  • Save bizouarn/78a9c3c4fd0ff4302c69f5331917ed61 to your computer and use it in GitHub Desktop.
Save bizouarn/78a9c3c4fd0ff4302c69f5331917ed61 to your computer and use it in GitHub Desktop.
Les Principes de la méthode SOLID

Les Principes de la méthode SOLID

Les principes SOLID sont un ensemble de directives de conception logicielle qui visent à créer des systèmes logiciels flexibles, extensibles et faciles à maintenir.
Chacun de ces principes représente un concept clé pour la création de code de qualité.
Dans ce document, nous allons explorer ces principes avec des exemples de code C#.

S pour SRP (Single Responsibility Principle) ou principe de Responsabilité Unique

Le SRP stipule qu'une classe devrait avoir une seule raison de changer.
En d'autres termes, une classe ne doit avoir qu'une seule responsabilité.

Ce principe garanti que chaque classe a une seule raison de changer, ce qui simplifie la maintenance et la compréhension du code.

En se concentrant sur une seule responsabilité, une classe devient plus modulaire, ce qui facilite la réutilisation et l'extension du code. En fin de compte, cela réduit les erreurs de logiciel et améliore la qualité globale du code.

classDiagram
    class Employee {
        + CalculateSalary()
    }
    
    class ReportGenerator {
        + GenerateReport()
    }

    Employee --|> ReportGenerator : uses
Loading
// Mauvaise pratique : Une classe avec plusieurs responsabilités
class Employee
{
    public void CalculateSalary() { /* Calcul du salaire */ }
    public void GenerateReport() { /* Génération de rapport */ }
}

// Bonne pratique : Séparation des responsabilités
class Employee
{
    public void CalculateSalary() { /* Calcul du salaire */ }
}

class ReportGenerator
{
    public void GenerateReport() { /* Génération de rapport */ }
}

O pour OCP (Open/Closed Principle) ou principe Ouvert/Fermé

Le OCP stipule qu'une classe devrait être ouverte à l'extension, mais fermée à la modification.
Cela signifie que vous pouvez ajouter de nouvelles fonctionnalités sans changer le code existant.

Ce principe vise à rendre les systèmes logiciels plus extensibles sans avoir à modifier le code existant.
En respectant ce principe, les développeurs peuvent ajouter de nouvelles fonctionnalités en créant de nouveaux modules plutôt qu'en modifiant ceux qui existent déjà.

Cela réduit les risques d'introduire des bugs dans le code existant et permet une évolution plus fluide du logiciel au fil du temps.

classDiagram
    class IShape {
        <<interface>>
        + Area(): double
    }

    class Circle {
        - Radius: double
        + Area(): double
    }

    class Rectangle {
        - Width: double
        - Height: double
        + Area(): double
    }

    IShape <|.. Circle
    IShape <|.. Rectangle
Loading
// Mauvaise pratique : Modification directe
class Circle
{
    public double Radius { get; set; }
}

// Pour ajouter un nouveau type de forme, il faut modifier la classe Circle.
// Cela viole le OCP.

// Bonne pratique : Utilisation de l'abstraction
interface IShape
{
    double Area();
}

class Circle : IShape
{
    public double Radius { get; set; }

    public double Area()
    {
        return Math.PI * Radius * Radius;
    }
}

class Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public double Area()
    {
        return Width * Height;
    }
}

L pour LSP (Liskov Substitution Principle) ou principe de Substitution de Liskov

Le LSP stipule que les objets de sous-classes doivent pouvoir être utilisés sans connaître leur classe de base.
En d'autres termes, une instance de sous-classe doit pouvoir remplacer une instance de classe de base sans provoquer d'erreurs.

Ce principe assure que les sous-classes peuvent être utilisées de manière interchangeable avec leurs classes de base sans provoquer d'erreurs. L'intérêt principal est d'assurer la cohérence du comportement entre les classes dérivées et les classes de base, ce qui facilite l'extension de l'application tout en maintenant une logique prévisible. Cela permet également une meilleure modularité et facilite les tests unitaires.

classDiagram
    class IFlyable {
        <<interface>>
        + Fly()
    }

    class Sparrow {
        + Fly()
    }

    class Ostrich

    IFlyable <|-- Sparrow
    Bird <|-- Sparrow
    Bird <|-- Ostrich
Loading
// Mauvaise pratique : Violation du LSP
class Bird
{
    public virtual void Fly() { /* Voler */ }
}

class Ostrich : Bird
{
    public override void Fly()
    {
        throw new InvalidOperationException("Les autruches ne volent pas.");
    }
}

// Bonne pratique : Respect du LSP
interface IFlyable
{
    void Fly();
}

class Sparrow : Bird, IFlyable
{
    public void Fly() { /* Voler */ }
}

class Ostrich : Bird
{
    // Les autruches ne volent pas, mais cela ne provoque pas d'erreur.
}

I pour ISP (Interface Segregation Principle) ou principe de Ségrégation de l'Interface

Le ISP stipule qu'il vaut mieux avoir plusieurs interfaces spécifiques qu'une interface générique.
Une classe ne doit pas être forcée d'implémenter des méthodes qu'elle n'utilise pas.

Ce principe favorise la création d'interfaces spécifiques aux besoins plutôt que d'avoir une interface générique. Cela permet aux clients de dépendre uniquement des méthodes dont ils ont besoin, ce qui évite le couplage indésirable.
L'intérêt est de réduire la complexité des interfaces, d'encourager la réutilisation des composants et d'améliorer la clarté et la flexibilité du code.

classDiagram
    class IWorker {
        <<interface>>
        + Work()
    }

    class IEater {
        <<interface>>
        + Eat()
    }

    class Robot {
        + Work()
    }

    class Human {
        + Work()
        + Eat()
    }

    IWorker <|-- Robot
    IWorker <|-- Human
    IEater <|-- Human
Loading
// Mauvaise pratique : Interface générique
interface IWorker
{
    void Work();
    void Eat();
}

class Robot : IWorker
{
    public void Work() { /* Travail du robot */ }
    public void Eat() { /* Manger du robot */ }
}

// Bonne pratique : Interfaces spécifiques
interface IWorker
{
    void Work();
}

interface IEater
{
    void Eat();
}

class Robot : IWorker
{
    public void Work() { /* Travail du robot */ }
}

class Human : IWorker, IEater
{
    public void Work() { /* Travail de l'humain */ }
    public void Eat() { /* Manger de l'humain */ }
}

D pour DIP (Dependency Inversion Principle) ou principe d'Inversion de Dépendance

Le DIP stipule que les modules de haut niveau ne doivent pas dépendre des modules de bas niveau, mais plutôt des abstractions.
Les détails doivent dépendre des abstractions.

Ce principe favorise la réduction des couplages, améliore la maintenabilité du code et facilite l'extension du système. En intégrant le DIP dans votre architecture logicielle, vous encouragez une séparation claire des préoccupations et une gestion plus efficace des dépendances.

classDiagram
    class ISwitchable {
        <<interface>>
        + TurnOn()
        + TurnOff()
    }

    class LightBulb {
        + TurnOn()
        + TurnOff()
    }

    class Switch {
        - device: ISwitchable
        + Switch(ISwitchable device)
        + Operate()
    }

    ISwitchable <|-- LightBulb
    Switch o-- ISwitchable : uses
Loading
// Mauvaise pratique : Dépendance directe
class LightBulb
{
    public void TurnOn() { /* Allumer l'ampoule */ }
    public void TurnOff() { /* Éteindre l'ampoule */ }
}

class Switch
{
    private LightBulb bulb = new LightBulb();

    public void Operate()
    {
        bulb.TurnOn();
    }
}

// Bonne pratique : Utilisation d'abstractions
interface ISwitchable
{
    void TurnOn();
    void TurnOff();
}

class LightBulb : ISwitchable
{
    public void TurnOn() { /* Allumer l'ampoule */ }
    public void TurnOff() { /* Éteindre l'ampoule */ }
}

class Switch
{
    private ISwitchable device;

    public Switch(ISwitchable device)
    {
        this.device = device;
    }

    public void Operate()
    {
        device.TurnOn();
    }
}

Il s'intègre parfaitement et facilement dans des modèles architecturaux tels que le MVVM. Avec des frameworks comme Prism, permettant ainsi de créer des applications .NET de haute qualité tout en respectant les bonnes pratiques de conception logicielle.

Conclusion

Les Principes SOLID constituent un ensemble fondamental de directives de conception logicielle qui visent à créer des systèmes logiciels robustes, maintenables et évolutifs.

En respectant ces principes, les développeurs peuvent créer des architectures logicielles plus solides, réduire les risques de bugs, améliorer la maintenabilité et favoriser la réutilisation du code.
Bien que l'application stricte de ces principes puisse parfois nécessiter un effort initial supplémentaire, les avantages à long terme en termes de qualité du logiciel et de réduction des coûts de maintenance en font un investissement précieux.

Les Principes SOLID ne sont pas des règles rigides, mais plutôt des lignes directrices qui doivent être adaptées en fonction des besoins spécifiques de chaque projet. En comprenant ces principes et en les appliquant judicieusement, les développeurs sont mieux armés pour créer des logiciels qui résistent à l'épreuve du temps, évoluent avec flexibilité et répondent aux besoins changeants des utilisateurs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment