SOLID
De Banane Atomic
Aller à la navigationAller à la recherche
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
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"); |