Principes SOLID : développement logiciel résumé
10 cardsLes principes SOLID expliqués, avec des exemples et des illustrations.
10 cards
Principes SOLID en Ingénierie Logicielle
Les principes SOLID sont un ensemble de concepts clés dans le développement logiciel, visant à favoriser la conception de code robuste, évolutif et maintenable [Source 4]. Ces principes sont largement reconnus et enseignés dans des contextes universitaires comme l'Université Lyon 1, l'Université Nice Sophia Antipolis, Polytechnique Montréal, l'Université Aix Marseille et l'UQAM, souvent en conjonction avec les patrons de conception [Source 2].
Les cinq principes SOLID
L'acronyme SOLID représente cinq principes fondamentaux de conception orientée objet [Source 3]:
- Single-Responsibility Principle (SRP) : Principe de Responsabilité Unique
- Open/Closed Principle (OCP) : Principe Ouvert/Fermé
- Liskov Substitution Principle (LSP) : Principe de Substitution de Liskov
- Interface Segregation Principle (ISP) : Principe de Ségrégation des Interfaces
- Dependency Inversion Principle (DIP) : Principe d'Inversion des Dépendances
1. Principe de Responsabilité Unique (SRP)
Le Single Responsibility Principle (SRP) stipule qu'une classe ne devrait avoir qu'une seule raison de changer [Source 5, 2]. En d'autres termes, une classe doit avoir une seule responsabilité. Si une classe a plusieurs responsabilités, elle devient plus complexe à maintenir et à faire évoluer.
Exemple de violation du SRP
Imaginons une classe C Library qui gère à la fois les enregistrements d'étudiants et la gestion des documents [Source 6]:
registerStudent(s: String): BooleanunregisterStudent(s: String): BooleanaddDocument(title: String, type: String): StringremoveDocument(id: String)borrowDocument(s: String, id: String): BooleanreturnDocument(id: String): Boolean
Cette classe a des responsabilités multiples (gestion des étudiants et gestion des documents). Une modification de la manière dont les étudiants sont enregistrés affecterait cette classe, de même qu'une modification de la gestion des documents. Cela viole le SRP.
2. Principe Ouvert/Fermé (OCP)
Le Open/Closed Principle (OCP) est défini par la citation suivante :
« Les entités logicielles (classes, packages, etc.) doivent être ouvertes à l'extension mais fermées à la modification »[Source 8].
Cela signifie que le comportement d'un module doit pouvoir être étendu sans modifier son code source existant. L'objectif est de permettre l'ajout de nouvelles fonctionnalités sans introduire de bogues dans le code déjà testé et stabilisé.
Exemple (implicite)
Bien que les images (img-1.jpeg, img-2.jpeg, img-3.jpeg) ne soient pas directement interprétables ici [Source 9, 10], elles illustrent probablement des architectures où de nouvelles fonctionnalités peuvent être ajoutées via l'héritage ou l'implémentation d'interfaces, sans toucher au code existant des classes de base ou des interfaces.
3. Principe de Substitution de Liskov (LSP)
Le Liskov Substitution Principle (LSP) est formulé comme suit :
« les sous-types doivent être substituables à leurs types de base. »[Source 11].
Plus formellement : Si S est un sous-type correct de T, alors pour chaque objet o1 de type S, il doit exister un objet o2 de type T tel que, pour tous les programmes P définis en termes de T, le comportement de P est inchangé lorsque o1 est remplacé par o2 [Source 11].
Conséquences de la violation du LSP
- Si une classe
Détend une classeB, un programmePconçu pour manipuler des instances deBdoit conserver le même comportement lorsqu'il manipule des instances deD[Source 12]. - Une violation se produit si une fonction
fqui prend un objet de typeBretourne un comportement incorrect lorsqu'on lui passe un objet de typeD. Dans ce cas, la classeDviole le LSP [Source 12].
Illustrations (implicites)
Les images (img-4.jpeg à img-10.jpeg) [Source 13, 14, 15] représentent probablement des diagrammes de classes ou des scénarios d'héritage qui pourraient soit respecter, soit violer le LSP. Par exemple, des classes dérivées qui modifient fondamentalement le contrat de leur classe parente seraient en violation.
4. Principe de Ségrégation des Interfaces (ISP)
Le Interface Segregation Principle (ISP) s'énonce ainsi :
« Les clients ne doivent pas être obligés de dépendre d'interfaces qu'ils n'utilisent pas. »[Source 16].
Il encourage la création d'interfaces petites et spécifiques plutôt que d'interfaces larges et génériques. Chaque client devrait se connecter uniquement aux méthodes qui lui sont pertinentes.
Exemple de violation de l'ISP
Considérons une interface unique pour une bibliothèque qui inclut de nombreuses méthodes [Source 17]:
addDocument(d: Document)removeDocument(d: Document)findDocumentByName(s: String): Optional<document>findDocumentById(s: String): Optional<document>canAccessTo(u: User): BooleanaddAdministrator(u: User)addRegisteredUser(i: User)createBorrowLog(u: User, s: User, b: Document)
Si un "client" (une classe) n'a besoin que de gérer les documents, il serait forcé d'implémenter ou de dépendre de méthodes liées aux utilisateurs ou aux journaux d'emprunt, qu'il n'utilise pas. La figure dans les sources [Source 18, 19] illustre probablement cette idée en montrant comment de grandes interfaces peuvent être décomposées en interfaces plus petites et plus spécifiques (par exemple, "findDocumentByName" pourrait être dans une interface IRechercheDocument, et "addAdministrator" dans une interface IGestionUtilisateur).
5. Principe d'Inversion des Dépendances (DIP)
Le Dependency Inversion Principle (DIP) propose que [Source 20]:
- Les modules de haut niveau ne doivent pas dépendre de ceux de bas niveau. Les deux doivent dépendre d'abstractions (par exemple, les interfaces).
- Les abstractions, elles, ne doivent pas dépendre des détails.
- Les détails (implémentations concrètes) doivent dépendre des abstractions.
Ce principe vise à découpler les modules, rendant les systèmes plus flexibles et testables.
Illustration du DIP
Prenons une classe A qui utilise directement une classe B d'un autre package. Cela crée une dépendance directe et non souhaitable [Source 21].
Pour appliquer le DIP [Source 22, 23, 24, 25]:
- Générer le comportement de
Ben une interfaceI. - S'assurer que
Bimplémente cette interfaceI. - Placer l'interface
Idans le package du module de haut niveau (par exemple, le package deA) qui contiendra toutes les méthodes queApeut appeler surB. - Remplacer toutes les références directes au type
Bpar des références à l'interfaceIdans la classeA.
Maintenant, la classe A dépend de l'abstraction I, et non plus directement de l'implémentation concrète B. Les images (img-12.jpeg à img-17.jpeg) illustrent ce processus de refactoring.
Comparaison avec GRASP
Les principes SOLID sont souvent discutés en conjonction ou en contraste avec d'autres principes de conception comme les principes GRASP (General Responsibility Assignment Software Patterns) [Source 26]. Alors que SOLID se concentre sur les relations au niveau des classes et des modules, GRASP se focalise sur l'attribution des responsabilités aux objets.
En résumé
Les principes SOLID procurent un guide essentiel pour bâtir des systèmes logiciels dont les qualités premières sont la flexibilité, la maintenabilité et l'extensibilité. Leur application conduit à des architectures plus robustes, capables de s'adapter aux changements et de faciliter le travail collaboratif.
Start a quiz
Test your knowledge with interactive questions