Principes SOLID : développement logiciel résumé

10 cards

Les principes SOLID expliqués, avec des exemples et des illustrations.

10 cards

Review
Spaced repetition shows you each card at the optimal time for long-term memorization, with increasingly spaced reviews.
Question
Que signifie l'acronyme SOLID?
Answer
Un acronyme pour cinq principes de conception : SRP, OCP, LSP, ISP, et DIP, favorisant un code robuste et maintenable.
Question
Quel est l'objectif des principes SOLID?
Answer
Favoriser la conception de code robuste, évolutif et maintenable.
Question
Énoncez le principe de responsabilité unique (SRP).
Answer
Une entité logicielle, comme une classe, ne doit avoir qu'une seule raison de changer, donc une seule responsabilité.
Question
Énoncez le principe ouvert/fermé (OCP).
Answer
Les entités logicielles doivent être ouvertes à l'extension mais fermées à la modification.
Question
Énoncez le principe de substitution de Liskov (LSP).
Answer
Les sous-types doivent pouvoir être substitués à leurs types de base sans altérer la cohérence du programme.
Question
Énoncez le principe de ségrégation des interfaces (ISP).
Answer
Les clients ne doivent pas être forcés de dépendre d'interfaces qu'ils n'utilisent pas.
Question
Énoncez le principe d'inversion de dépendance (DIP).
Answer
Les modules de haut niveau ne doivent pas dépendre de ceux de bas niveau. Tous deux doivent dépendre d'abstractions.
Question
Dans le DIP, de quoi les détails doivent-ils dépendre?
Answer
Les détails, ou implémentations concrètes, doivent dépendre des abstractions, et non l'inverse.
Question
Que se passe-t-il si le principe LSP est violé?
Answer
Passer un sous-type à une fonction attendant le type de base peut entraîner un comportement incorrect du programme.
Question
Comment le DIP préconise-t-il de lier les modules?
Answer
En faisant dépendre les modules de haut et bas niveau d'abstractions partagées, comme des interfaces.

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): Boolean
  • unregisterStudent(s: String): Boolean
  • addDocument(title: String, type: String): String
  • removeDocument(id: String)
  • borrowDocument(s: String, id: String): Boolean
  • returnDocument(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 classe B, un programme P conçu pour manipuler des instances de B doit conserver le même comportement lorsqu'il manipule des instances de D [Source 12].
  • Une violation se produit si une fonction f qui prend un objet de type B retourne un comportement incorrect lorsqu'on lui passe un objet de type D. Dans ce cas, la classe D viole 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): Boolean
  • addAdministrator(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]:

  1. Générer le comportement de B en une interface I.
  2. S'assurer que B implémente cette interface I.
  3. Placer l'interface I dans le package du module de haut niveau (par exemple, le package de A) qui contiendra toutes les méthodes que A peut appeler sur B.
  4. Remplacer toutes les références directes au type B par des références à l'interface I dans la classe A.

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