It seems like you are using an ad blocker. To enhance your experience and support our website, please consider:

  1. Signing in to disable ads on our site.
  2. Sign up if you don't have an account yet.
  3. Or, disable your ad blocker by doing this:
    • Click on the ad blocker icon in your browser's toolbar.
    • Select "Pause on this site" or a similar option for lancecourse.com.

Principe SOLID Expliqué — En TypeScript

By zooboole

Dans le monde du développement logiciel, produire du code qui est non seulement fonctionnel, mais aussi maintenable, évolutif et adaptable est d'une importance capitale. Les principes SOLID, un ensemble de cinq principes de conception, fournissent des lignes directrices pour atteindre ces objectifs. Ces principes ont été introduits par Robert C. Martin et sont depuis devenus un pilier de la programmation orientée objet. Dans cet article, nous explorerons chacun des principes SOLID et démontrerons leur application en utilisant TypeScript.

1. Principe de Responsabilité Unique (SRP)

Le Principe de Responsabilité Unique stipule qu'une classe ne devrait avoir qu'une seule raison de changer. En d'autres termes, une classe devrait avoir une seule responsabilité ou tâche. Cela garantit que la classe reste focalisée et ne devienne pas encombrée avec des fonctionnalités non liées.

Voyons un exemple de violation du SRP et de sa refonte en TypeScript :

// Violation du SRP
class Utilisateur {
    constructor(private nom: string) {}

    getNom() {
        return this.nom;
    }

    enregistrerEnBaseDeDonnees() {
        // Code pour enregistrer l'utilisateur en base de données
    }
}

Dans le code ci-dessus, la classe Utilisateur a deux responsabilités : gérer les données de l'utilisateur et interagir avec la base de données. Cela viole le SRP. Réorganisons-le :

// Application du SRP
class Utilisateur {
    constructor(private nom: string) {}

    getNom() {
        return this.nom;
    }
}

class RepertoireUtilisateurs {
    enregistrerEnBaseDeDonnees(utilisateur: Utilisateur) {
        // Code pour enregistrer l'utilisateur en base de données
    }
}

En séparant les responsabilités en classes distinctes, nous respectons le SRP, ce qui rend le code plus facile à maintenir et à étendre.

2. Principe Ouvert/Fermé (OCP)

Le Principe Ouvert/Fermé met l'accent sur le fait que les entités logicielles (classes, modules, fonctions) devraient être ouvertes à l'extension mais fermées à la modification. Cela signifie que vous devriez être en mesure d'ajouter de nouvelles fonctionnalités sans modifier le code existant.

Illustrons le OCP à l'aide d'un exemple TypeScript :

// Violation du OCP
class Rectangle {
    constructor(public largeur: number, public hauteur: number) {}
}

class CalculateurAire {
    calculerAire(forme: Rectangle) {
        return forme.largeur * forme.hauteur;
    }
}

Ici, le CalculateurAire est étroitement couplé à la classe Rectangle, ce qui rend difficile l'ajout de nouvelles formes sans modifier la classe CalculateurAire. Réorganisons-le :

// Application du OCP
interface Forme {
    calculerAire(): number;
}

class Rectangle implements Forme {
    constructor(public largeur: number, public hauteur: number) {}

    calculerAire() {
        return this.largeur * this.hauteur;
    }
}

class Cercle implements Forme {
    constructor(public rayon: number) {}

    calculerAire() {
        return Math.PI * this.rayon * this.rayon;
    }
}

En utilisant l'interface Forme et en créant des classes de forme individuelles, nous respectons le OCP, permettant une extension facile sans modifier le code existant.

3. Principe de Substitution de Liskov (LSP)

Le Principe de Substitution de Liskov stipule que les objets d'une classe parente devraient pouvoir être substitués par des objets de ses classes filles sans affecter la correction du programme. En d'autres termes, les classes dérivées devraient pouvoir substituer leurs classes de base de manière transparente.

Illustrons le LSP avec TypeScript :

// Violation du LSP
class Oiseau {
    voler() {
        // Code pour voler
    }
}

class Autruche extends Oiseau {
    // Les autruches ne peuvent pas voler
}

function faireVolerOiseau(oiseau: Oiseau) {
    oiseau.voler();
}

Dans ce cas, la classe Autruche hérite de Oiseau, mais elle ne prend pas en charge le comportement de vol. Cela viole le LSP. Corrigeons cela :

// Application du LSP
interface Volant {
    voler(): void;
}

class Oiseau implements Volant {
    voler() {
        // Code pour voler
    }
}

class Autruche {
    // Comportement spécifique aux autruches
}

function faireVolOiseau(volant: Volant) {
    volant.voler();
}

En séparant le comportement de vol en une interface et en permettant uniquement aux objets volants de l'utiliser, nous respectons le LSP.

4. Principe de Ségrégation de l'Interface (ISP)

Le Principe de Ségrégation de l'Interface suggère que les clients ne devraient pas être contraints de dépendre d'interfaces qu'ils n'utilisent pas. En d'autres termes, les interfaces volumineuses devraient être divisées en interfaces plus petites et plus spécifiques pour éviter les dépendances inutiles.

Appliquons l'ISP en TypeScript :


// Violation de l'ISP
interface Travailleur {
    travailler(): void;
    manger(): void;
}

class EmployeBureau implements Travailleur {
    travailler() {
        // Code pour travailler au bureau
    }

    manger() {
        // Code pour manger pendant la pause
    }
}

class EmployeUsine implements Travailleur {
    travailler() {
        // Code pour travailler à l'usine
    }

    manger() {
        // Code pour manger pendant la pause
    }
}

Ici, à la fois EmployeBureau et EmployeUsine sont contraints de mettre en œuvre des méthodes qu'ils n'utilisent pas. Réparons cela :


// Application de l'ISP
interface Travaillable {
    travailler(): void;
}

interface Mangeable {
    manger(): void;
}

class EmployeBureau implements Travaillable, Mangeable {
    travailler() {
        // Code pour travailler au bureau
    }

    manger() {
        // Code pour manger pendant la pause
    }
}

class EmployeUsine implements Travaillable {
    travailler() {
        // Code pour travailler à l'usine
    }
}

En divisant l'interface Travailleur en Travaillable et Mangeable, nous nous assurons que les classes ne mettent en œuvre que les méthodes dont elles ont besoin, respectant ainsi l'ISP.

5. Principe d'Inversion de Dépendance (DIP)

Le Principe d'Inversion de Dépendance stipule que les modules de haut niveau ne devraient pas dépendre des modules de bas niveau ; les deux devraient dépendre d'abstractions. De plus, les abstractions ne devraient pas dépendre des détails ; les détails devraient dépendre des abstractions.

Voyons un exemple de DIP en TypeScript :

// Violation du DIP
class Ampoule {
    allumer() {
        // Code pour allumer l'ampoule
    }

    éteindre() {
        // Code pour éteindre l'ampoule
    }
}

class Interrupteur {
    private ampoule = new Ampoule();

    basculer() {
        if (/* condition */) {
            this.ampoule.allumer();
        } else {
            this.ampoule.éteindre();
        }
    }
}

Dans ce scénario, la classe Interrupteur dépend directement de Ampoule, ce qui viole le DIP. Améliorons cela :

// Application du DIP
interface Commutable {
    allumer(): void;
    éteindre(): void;
}

class Ampoule implements Commutable {
    allumer() {
        // Code pour allumer l'ampoule
    }

    éteindre() {
        // Code pour éteindre l'ampoule
    }
}

class Interrupteur {
    private appareil: Commutable;

    constructor(appareil: Commutable) {
        this.appareil = appareil;
    }

    basculer() {
        if (/* condition */) {
            this.appareil.allumer();
        } else {
            this.appareil.éteindre();
        }
    }
}

En introduisant l'interface Commutable, nous avons inversé la direction de la dépendance, respectant ainsi le DIP.

Conclusion

Les principes SOLID fournissent une base pour écrire un code propre, maintenable et flexible. En comprenant et en appliquant ces principes, les développeurs peuvent créer des systèmes plus robustes, plus faciles à étendre et moins sujets aux bugs. Dans cet article, nous avons exploré chacun des principes SOLID — Principe de Responsabilité Unique, Principe Ouvert/Fermé, Principe de Substitution de Liskov, Principe de Ségrégation de l'Interface et Principe d'Inversion de Dépendance — et démontré leur mise en œuvre à l'aide de TypeScript. Intégrer ces principes dans votre processus de conception et de développement peut considérablement améliorer la qualité et la longévité de vos projets logiciels.

Last updated 2024-01-11 UTC