« SOLID » : différence entre les versions
De Banane Atomic
Aller à la navigationAller à la recherche
Ligne 28 : | Ligne 28 : | ||
log.Error("message d'erreur"); | log.Error("message d'erreur"); | ||
</kode> | </kode> | ||
* 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). Ces dépendances peuvent être externalisées. | |||
= Open Closed Principle = | = Open Closed Principle = |
Version du 3 avril 2022 à 19:25
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). 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 é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.
abstract class Contact { public abstract string PhoneCountryCode { get; } } class FrenchContact : Contact { public override string PhoneCountryCode { get { return "33"; } } } class NoCountryContact : Contact { public override string PhoneCountryCode { get { 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"); |