Aller au contenu

Les principes SOLID, pour quoi faire ?

On vous demande d'appliquer les principes SOLID, mais vous avez du mal à en comprendre l'intérêt? Cet article est fait pour vous!

Rangeons le code spaghettis au placard grâce aux principes SOLID

Les principes SOLID constituent cinq principes de développement qui permettent d'améliorer grandement la qualité du code.

Ne vous êtes-vous jamais retrouvé à devoir déboguer une méthode complètement confuse longue de 2000 lignes avec des if/else if/else un peu partout? Ou bien devoir comprendre un code spaghettis avec plein de variables entremêlées, redondantes, parfois nulles?

Personnellement, quand je dois modifier ce genre de code, j'ai la petite goutte de sueur et je me demande si la moindre modification ne va pas tout détruire tel un château de cartes qui s'effondre...

Les principes SOLID existent justement pour ne pas rencontrer de telles situations qui sont un enfer pour la plupart des développeurs !

S Comme Single-Responsibility

Ce premier principe impose que chaque entité ait son rôle unique ! Par exemple, une méthode ne doit faire qu'une seule chose. Cela permet d'éviter justement de se poser la question "Mais dois-je vraiment appeler cette méthode ? Y aura-t-il des effets de bords ?"

Adieu donc, les méthodes couteau-suisse dont le nom décrit bien qu'elles savent tout faire "readDetailsAndCreateElementOrUpdateIfExists". En appliquant ce principe, chaque élément de votre code traite un unique besoin : readDetails, create, update, exists

O Comme Open-Closed

Notre code doit être ouvert à l'extension mais fermé à la modification. Cela permet par exemple d'éviter le code spaghettis et donc d'améliorer la lisibilité et la robustesse.

On évite ainsi les enchaînements de if-else et on utilise l'héritage !

À ne pas faire !

Ici, comment gèrera-t-on une nouvelle opération ? Faudra-il la placer avant l'addition ? Après l'addition ? En dernier ? En premier ?

    public static double compute(BiOperation biOperation){
        if(biOperation.type() == ADDITION){
            return biOperation.left+biOperation.right;
        }
        if(biOperation.type() == SUBSTRACTION){
            return biOperation.left-biOperation.right;
        }
        // last case is division
        if(biOperation.right == 0.0){
            throw new OperationException("You cannot divide by 0!");
        }
        return biOperation.left/biOperation.right;
    }

À adopter !

    public static double compute(BiOperation biOperation){
        return biOperation.compute();
    }

    interface BiOperation {
        double compute();
    }

    record Addition(double left, double right) implements BiOperation{
        @Override
        public double compute() {
            return left+right;
        }
    }
    record Substraction(double left, double right) implements BiOperation{
        @Override
        public double compute() {
            return left-right;
        }
    }
    record Division(double left, double right) implements BiOperation{
        @Override
        public double compute() {
            if(right == 0.0){
                throw new OperationException("You cannot divide by 0!");
            }
            return left/right;
        }
    }

L comme Liskov Substitution

Quelles que soient les sous-classes d'une classe mère ou d'une abstraction, elles ne doivent pas enfreindre le comportement de cette dernière. Cela permet de gagner en lisibilité et en robustesse. En effet, le lecteur n'a pas besoin de connaître l'implémentation concrète d'une classe fille pour comprendre ce que fera l'appel de méthode. Un remplacement par une sous-classe n'engendrera pas d'effets de bord non désirés.

I comme Interface Segregation

Pour la lisibilité et la fiabilité, il est important de construire des interfaces qui ne répondent qu'à un seul besoin.

Pour illustrer cela : sur un double clic de souris, vous aimeriez afficher "double clic". Le framework que vous utilisez contient une classe MouseListener qui contient trois méthodes à implémenter : mouseDoubleClick(MouseEvent e), mouseDown(MouseEvent e) et mouseUp(MouseEvent e). Vous devrez fournir une implémentation vide des méthodes "mouseUp" et "mouseDown", ce qui gêne la lisibilité (Sonar vous explique pourquoi).

D comme Dependency Inversion

Ce principe demande de dépendre au maximum des abstractions plutôt que des implémentations concrètes. C'est ce principe qui apporte la modularité dans votre code. Puisque vous dépendez des abstractions, il devient facile et non-impactant de modifier une implémentation concrète. Le coeur de votre application est alors complètement isolé de ses choix techniques.

Conclusion : Les principes SOLID, des principes à appliquer sans modération

Les principes SOLID constituent la trousse de survie des développeurs. Ils rendent le code plus lisible, plus fiable, plus maintenable et permettent d'éviter des mauvaises surprises. Pour chaque développement, il est crucial de se demander systématiquement "mon code est-il conforme aux principes SOLID ?". Malheureusement, les codes qui mériteraient d'être revus afin de respecter ces principes sont légion, mais les appliquer systématiquement rendra vos projets bien plus faciles à maintenir.

Dernier