« SOLID » : différence entre les versions

De Banane Atomic
Aller à la navigationAller à la recherche
 
Aucun résumé des modifications
 
(7 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 28 : Ligne 31 :
         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, fichier de log). Ces dépendances peuvent être externalisées.


= Open Closed Principle =
= Open Closed Principle =
Ligne 60 : 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 72 : Ligne 79 :
class FrenchContact : Contact
class FrenchContact : Contact
{
{
     public override string PhoneCountryCode { get { return "33"; } }
     public override string PhoneCountryCode => "33";
}
}


class NoCountryContact : Contact
class NoCountryContact : Contact
{
{
     public override string PhoneCountryCode { get { throw new InvalidOperationException(); } }
     public override string PhoneCountryCode => throw new InvalidOperationException();
}
}


Ligne 91 : Ligne 98 :
</kode>
</kode>


= Interface segregation principle =
= 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 138 : 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é

Csharp.svg
// 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).

Csharp.svg
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.

Csharp.svg
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.

Csharp.svg
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).

Csharp.svg
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");