« SOLID » : différence entre les versions
Aucun résumé des modifications |
|||
(5 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
[[Category:Informatique_Générale]] | [[Category:Informatique_Générale]] | ||
= Liens = | |||
* [https://philippe.developpez.com/articles/SOLIDdotNet/ Bonnes pratiques objet en .net : Introduction aux principes SOLID] | |||
= Définition = | = Définition = | ||
Acronyme définissant les 5 premiers principes de la [[POO|programmation orientée objet]]. | Acronyme définissant les 5 premiers principes de la [[POO|programmation orientée objet]]. | ||
Ligne 63 : | Ligne 66 : | ||
PhoneCountryCode = "33"; | PhoneCountryCode = "33"; | ||
</kode> | </kode> | ||
Les 3 solutions permettent étendre le code pour gérer de nouveaux pays sans avoir à modifier la classe Contact. | Les 3 solutions permettent d'étendre le code pour gérer de nouveaux pays sans avoir à modifier la classe Contact. | ||
= Liskov Substitution Principle = | = Liskov Substitution Principle = | ||
Les sous classes doivent respecter le contrat de leur classe parente. | Les sous-classes doivent respecter le contrat de leur classe parente.<br> | ||
Les sous-classes doivent être substituables par leur classe de base sans que cela modifie le comportement du programme. | |||
<kode lang='csharp'> | <kode lang='csharp'> | ||
abstract class Contact | abstract class Contact | ||
Ligne 75 : | Ligne 79 : | ||
class FrenchContact : Contact | class FrenchContact : Contact | ||
{ | { | ||
public override string PhoneCountryCode | public override string PhoneCountryCode => "33"; | ||
} | } | ||
class NoCountryContact : Contact | class NoCountryContact : Contact | ||
{ | { | ||
public override string PhoneCountryCode | public override string PhoneCountryCode => throw new InvalidOperationException(); | ||
} | } | ||
Ligne 94 : | Ligne 98 : | ||
</kode> | </kode> | ||
= Interface | = Interface Segregation Principle = | ||
Les classes ne doivent pas implémenter d'interfaces dont elles n'utilisent pas toutes les méthodes.<br /> | Les classes ne doivent pas implémenter d'interfaces dont elles n'utilisent pas toutes les méthodes.<br /> | ||
Découper les interfaces pour qu'elles correspondent au besoin. | Découper les interfaces pour qu'elles correspondent au besoin. | ||
Ligne 141 : | Ligne 145 : | ||
= Dependency Inversion Principle = | = Dependency Inversion Principle = | ||
Le code business ne doit pas dépendre du code de bas niveau (accès à la bdd, log, appels web) mais utiliser des d'abstractions (injection d'interfaces). | |||
<kode lang='csharp'> | <kode lang='csharp'> | ||
class FileLog | class FileLog |
Dernière version du 3 avril 2022 à 19:53
Liens
Définition
Acronyme définissant les 5 premiers principes de la programmation orientée objet.
Single Responsibility Principle | Une classe ne doit avoir qu'une seule responsabilité. |
Open Close Principle | Une classe doit pouvoir être étendue sans devoir être modifiée. |
Liskov Principle | Les sous classes doivent respecter le contrat de leur classe parente. |
Interface Segregation Principle | Découper les interfaces pour que les classes qui les implémentent utilisent toutes les méthodes des interfaces. |
Dependency Inversion Principle |
Single Responsibility Principle
Une classe ne doit avoir qu'une seule responsabilité
// la classe Contact est responsable de la manipulation des contacts class Contact { public Contact() { // elle ne doit pas se charger de la manière dont les messages de log sont écris File.AppendAllText(@"C:\Error.log", "message d'erreur"); // mais plutôt faire appel à un gestionnaire de log log.Error("message d'erreur"); |
- lister les méthodes et essayer de les grouper par noms ou actions. Si plusieurs groupes sont identifiés, c'est un indicateur que la classe peut être redécoupée.
- lister les dépendances externes (appel direct à la bdd, API, fichier de log). Ces dépendances peuvent être externalisées.
Open Closed Principle
Une classe, une méthode doit pouvoir être étendue (Open for extension) sans pour cela devoir être modifiée (closed for modification).
class Contact { public Contact(Countries country) { // si l'on veut étendre le code pour gérer de nouveaux pays on va devoir modifier le code de Contact if (country == Countries.France) { PhoneCountryCode = "33"; } else if (country == Countries.Switzerland) { PhoneCountryCode = "41"; } // Solution 1 : injecter le PhoneCountryCode public Contact(Countries country, string phoneCountryCode) { PhoneCountryCode = phoneCountryCode; // Solution 2 : utiliser une factory public Contact(Countries country) { PhoneCountryCode = PhoneCountryCodeFactory.GetPhoneCountryCode(country); // Solution 3 : polymorphisme class FrenchContact : Contact { public FrenchContact() { PhoneCountryCode = "33"; |
Les 3 solutions permettent d'étendre le code pour gérer de nouveaux pays sans avoir à modifier la classe Contact.
Liskov Substitution Principle
Les sous-classes doivent respecter le contrat de leur classe parente.
Les sous-classes doivent être substituables par leur classe de base sans que cela modifie le comportement du programme.
abstract class Contact { public abstract string PhoneCountryCode { get; } } class FrenchContact : Contact { public override string PhoneCountryCode => "33"; } class NoCountryContact : Contact { public override string PhoneCountryCode => throw new InvalidOperationException(); } var contactList = new List<Contact>(); cl.Add(new FrenchContact()); cl.Add(new NoCountryContact()); foreach (var contact in contactList) { // NoCountryContact.PhoneCountryCode va générer une InvalidOperationException Console.WriteLine(contact.PhoneCountryCode); } |
Interface Segregation Principle
Les classes ne doivent pas implémenter d'interfaces dont elles n'utilisent pas toutes les méthodes.
Découper les interfaces pour qu'elles correspondent au besoin.
interface IDatabaseAccess { void Load(); void Save(); } class Contact : IDatabaseAccess { public void Load() { /* */ } public void Save() { /* */ } } class ReadOnlyContact : IDatabaseAccess { public void Load() { /* */ } // Ici on ne veut pas implémenter la méthode Save car le contact est en lecture seule public void Save() { /* */ } } // Solution: on découpe l'interface IDatabaseAccess interface IDatabaseAccessLoad { void Load(); } interface IDatabaseAccess : IDatabaseAccessLoad { void Save(); } class Contact : IDatabaseAccess { public void Load() { /* */ } public void Save() { /* */ } } class ReadOnlyContact : IDatabaseAccessLoad { public void Load() { /* */ } } |
Dependency Inversion Principle
Le code business ne doit pas dépendre du code de bas niveau (accès à la bdd, log, appels web) mais utiliser des d'abstractions (injection d'interfaces).
class FileLog { public static void Log(string message) { File.AppendAllText(@"C:\Error.log", message); } } class Contact { // Contact dépend de FileLog private FileLog _log = new FileLog(); public Contact() { _log.Log("message"); // Solution: utiliser une interface et injecter la dépendance class FileLog : ILog class Contact { private ILog _log; public Contact(ILog log) { _log = log; _log.Log("message"); |