Aller au contenu

Ségrégation des interfaces (ISP)

Ségrégation des interfaces (ISP)

Introduction

Une interface ne doit pas imposer à ses implémentations des membres dont elles n'ont pas besoin. Il est préférable de définir plusieurs interfaces plus spécialisées, plutôt qu'une interface unique regroupant des responsabilités diverses. Avec ce principe ISP, les interfaces contiennent moins de membres et sont plus cohérentes. Chaque classe dépend uniquement des fonctionnalités qu'elle utilise réellement. Le code devient ainsi plus lisible, plus maintenable et plus facile à faire évoluer.

Exemple qui ne respecte pas ce principe de ségrégation des interfaces

Soit une interface regroupant plusieurs traitements pouvant être réalisés sur une facture :

public interface IInvoiceProcessor
{
    void SendEmail(Invoice invoice);

    void Print(Invoice invoice);
}

Supposons maintenant que la fonctionnalité de l'application envoie uniquement des e-mails.

public class InvoiceProcessor : IInvoiceProcessor
{
    public void SendEmail(Invoice invoice)
    {
        // Envoi de l'e-mail
    }

    public void Print(Invoice invoice)
    {
        throw new NotSupportedException("L'impression n'est pas prise en charge.");
    }
}

Cette implémentation pose un problème. La classe InvoiceProcessor est contrainte d'implémenter une méthode nommée Print, qu'elle n'utilise pas et qui ne correspond pas à sa responsabilité. Cette méthode n'ayant pas de sens pour cette classe, son implémentation consiste uniquement à lever une exception de type NotSupportedException.

Application de ce principe de ségrégation des interfaces

L'application de ce principe consiste à découper cette interface en deux interfaces spécialisées :

public interface IEmailSender
{
    void SendEmail(Invoice invoice);
}

public interface IInvoicePrinter
{
    void Print(Invoice invoice);
}

Les implémentations deviennent alors plus simples et chaque classe ne fournit que les fonctionnalités qui la concernent :

public class InvoiceProcessor : IEmailSender
{
    public void SendEmail(Invoice invoice)
    {
        // Envoi de l'e-mail
    }
}

public class InvoicePrinter : IInvoicePrinter
{
    public void Print(Invoice invoice)
    {
        // Impression
    }
}

Utilisation

Le besoin de la classe InvoiceService est uniquement d'envoyer les factures par e-mail. Ainsi, elle dépend uniquement de l'interface appropriée pour répondre à ce besoin.

public class InvoiceService
{
    private readonly IEmailSender _emailSender;

    public InvoiceService(IEmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public void SendInvoice(Invoice invoice)
    {
        _emailSender.SendEmail(invoice);
    }
}

Les évolutions concernant l'impression n'ont donc aucun impact sur cette classe, ce qui réduit le couplage et facilite la maintenance de l'application.