L'un des points clés du DevOps est d'avoir une bonne couverture de tests unitaires. Ces tests sont automatisés et rapides et garantissent un niveau de confiance supérieur dans la qualité du code de l'application. Mais si l'application est codée sans suivre certains principes, le développeur se retrouve vite bloqué pour créer des tests unitaires. Il y a alors un plafond du code coverage atteignable. Les principes SOLID vous permettent de résoudre ce problème. Ils apportent également d'autre bénéfices très importants, comme vous allez le voir.


La théorie des principes SOLID a été introduire Michael Feathers. Mais elle a été créée en 2000 par Robert Cecil Martin, alias Uncle Bob, dans "Design Principles and Design Patterns". Il est d'ailleurs co-auteur du Manifeste Agile. Son blog "Clean Code blog" se trouve à l'adresse suivante : https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start/.

Avant de présenter ce que c'est, voyons à quoi ils servent, quels sont les avantages que vous tirerez à les utiliser.

Le principal avantage est qu’ils permettent de rendre le code testable par des tests unitaires, et donc d’augmenter le code coverage. Ainsi, la pratique du TDD induit automatiquement l’application des principes SOLID. Le second avantage est qu’ils permettent de rendre le code plus pérenne.

Les principes SOLID se trouvent donc au coeur des pratiques Agile et du DevOps.

Pour bien comprendre les principes SOLID, nous allons utilisons présenter les 5 principes appliqués à une classe User.

Single Responsibility Principle (SRP)

Le principe « Une seule responsabilité » (« Single responbility ») :

  • Une classe ne doit avoir qu’une seule responsabilité.
  • Et tous ses services doivent être alignés sur cette responsabilité.

Exemple

Prenons un exemple. La classe User crée un utilisateur dans la base de données. Elle ne devrait pas par exemple gérer ses droits.

Comment trouver les violations du SRP ?

Les violations du SRP se trouvent plutôt dans les classes avec un haut niveau de couplage, car cela indique que de nombreuses méthodes de la classe utilisent des données différentes.

Comment les résoudre ?

Les Design Patterns Façade and Proxy résolvent souvent le problème.

Open / Closed Principle (OCP)

Le principe « Ouvert / fermé » (« Open / closed ») : Les objets doivent être ouverts mais fermés aux modifications. Quand on parle d’objet, on songe aux classes, aux méthodes, ...

Que signifie ouvert ? Cela signifie qu’on peut implémenter de nouvelles fonctionnalités en créant de nouvelles classes héritant de ces classes.

Que signifie fermé aux modifications ? Cela signifie que ces classes ne doivent pas être modifiées.

Exemple

Prenons un exemple. La classe User est l’objet générique. Elle ne peut pas être modifiée. Mais on peut créer une nouvelle classe Admin héritant de la classe User et implémentant de nouvelles fonctionnalités.

Comment trouver les violations de l’OCP ?

Les violations de l’OCP se trouvent souvent dans les successions de switch ou de if.

Comment les corriger ?

Isoler les portions de code qui varient dans une autre classe.

Liskov Substitution Principle (LSP)

Le principe « Substitution Liskov » (« Liskov substitution »): Soit un objet S de sous-type de T; les objets de type S peuvent se substituer aux objets de type T sans changer le comportement du programme. On parle de sous-typage fort.

Exemple

Voici un exemple. Les classes Admin et Member (type S) héritent de la classe User (type T). Chacun de ces deux classes doit implémenter l’interface complète de la classe User. Les classes Admin et Member (type S) peuvent donc se substituer à la classe User (type T).

Comment trouver les violations du LSP ?

Les violations du LSP se trouvent souvent dans les vérifications de type. Elles se trouvent aussi dans les sous-classes qui implémentent une méthode vide ou une simple exception.

Comment les corriger ?

Il y a deux solutions :

  • Modifier le contrat pour le parent pour que la sous-classe puisse le remplir.
  • Retirer de la sous-classe l’implémentation de la classe parente.

Parfois la solution est de :

  • Déplacer le code partagé de la classe parente vers une nouvelle classe.
  • Faire hériter les deux classes de cette nouvelle classe.

Interface Segregation Principle (ISP)

Le principe « Ségrégation de l’interface » (« Interface segregation ») :

Aucune classe S héritant de la classe T et implémentant son interface ne doit dépendre de méthodes de T qu’elle n’utilise pas. Si nécessaire, diviser l’interfaces en plus petites et plus spécifiques : La classe S ne verra que les méthodes qui l’intéresse. Cette classe est plus facile à refactorer, à modifier et à redéployer. On parle d’interface de rôle.

Exemple

Les classes Admin et Member héritent de la classe User. Si on utilisait une seule interface, la classe User devrait implémenter des méthodes utilisées par la classe Admin seulement. Donc on répartit les méthodes à implémenter dans deux interfaces spécifiques :

  • IUser pour les méthodes de base,
  • IAdmin pour les méthodes d’administration.

Comment trouver les violations de l’ISP ?

Les classes violant le ISP se trouvent souvent dans les classes ou interfaces ayant un grand nombre de méthodes. Ce sont souvent celles qui implémentent des méthodes vides uniquement pour satisfaire les interfaces.

Comment les corriger ?

Identifier les interfaces ayant des méthodes ne s’appliquant pas à toutes les implémentations.

Déplacer ces méthodes dans une nouvelle interface.

Supprimer les implémentations inutiles.

Dependency Inversion Principle (DIP)

Le principe « Inversion de dépendance » (« Dependency inversion ») :

Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Les deux devraient dépendre des abstractions.

Les abstractions ne devraient pas dépendre des détails. Les détails doivent dépendre des abstractions.

Les objets de haut et de bas doivent dépendre de la même abstraction.

Exemple

Prenons cette fois un contre-exemple, ce sera plus simple pour comprendre. Prenons le cas d'un bouton "Créer utilisateur" sur un écran. Quand le bouton est cliqué, une nouvelle instance d'une classe ManageUser est créée puis la méthode createUser() de la classe ManageUser est appelée. Dans la méthode createUser(), une nouvelle instance de la classe UserDB est crée, qui effectue les appels à la base de données.

 

Comment trouver les violations du DIP ?

Les classes violant le DIP posent des problèmes à être testées par des tests unitaires. C’est donc un moyen de les trouver.

Comment les corriger ?

Modifiez la classe testée pour qu’elle dépende d’une interface au lieu d’une concrète. Ensuite, pour tester cette classe, passez un mock à cette interface au lieu d’une classe.


Les principes SOLID augmentent le nombre d'objets, c'est vrai. Mais ils favorisent le découplage des objets, l'augmentation de la cohésion du code et une forte encapsulation. Et ces caractéristiques permettent de pouvoir augmenter le code coverage des tests unitaires bien sûr, ce qui n'est pas une fin en soi. Mais ils permettent aussi et surtout de rendre le code plus robuste, plus maintenable, plus rétuilisable et extensible.


Pensées pour mieux produire

Soyez prévenu dès que mon livre "Pensées pour mieux produire" sera disponible à la vente !

DevOps, Agile, Scrum, Kanban, XP, SAFe, LeSS, Lean Startup, Lean UX, Design Thinking, Craftmanship, Management 3.0, ...

 

Bruno Delb

Agile Coach and DevOps, with an experience in the Medical Device software domain, Management 3.0, Agile games and development (especially on mobile) are my passion.

Search

Ads