CodeGym /Cours /C# SELF /Implémentation de plusieurs interfaces

Implémentation de plusieurs interfaces

C# SELF
Niveau 23, Leçon 2
Disponible

1. Pourquoi implémenter plusieurs interfaces ?

Quand tu conçois un vrai système, les objets jouent souvent pas qu’un seul "rôle" — mais plusieurs à la fois. Imagine : t’as un livre électronique, que tu peux non seulement lire, mais aussi éditer, et même sauvegarder dans le cloud. En termes de programmation orientée objet (POO), ça veut dire que l’objet doit implémenter plusieurs interfaces d’un coup :

  • IReadable — lecture du contenu.
  • IWritable — édition du contenu.
  • ISyncable — synchronisation avec un stockage distant.

C’est là que la possibilité d’implémenter plusieurs interfaces entre en jeu. C’est un super-pouvoir de C# (et de la POO en général) que les classes n’ont pas : tu peux pas hériter de deux classes ou plus, mais tu peux implémenter autant d’interfaces que tu veux.

Une analogie dans la vraie vie

Imagine un employé dans une boîte. Vasya peut être en même temps :

  • Développeur (il code)
  • Testeur (il vérifie parfois le code des autres)
  • Manager (il planifie les sprints ou la dose de café du jour)

Ces "rôles" ont des responsabilités complètement différentes. Mais Vasya gère chaque contrat sans souci ! En programmation, c’est pareil : une classe implémente des interfaces et prend en charge leurs "responsabilités".

2. Syntaxe pour implémenter plusieurs interfaces

C’est super simple : quand tu déclares la classe, tu les listes séparés par une virgule :


public interface IReadable
{
    void Read();
}

public interface IWritable
{
    void Write(string text);
}

public class Note : IReadable, IWritable
{
    private string content = "";

    public void Read()
    {
        Console.WriteLine("Note : " + content);
    }

    public void Write(string text)
    {
        content = text;
        Console.WriteLine("Note mise à jour !");
    }
}

Dans cet exemple, la classe Note implémente les deux interfaces. Donc elle doit avoir les deux méthodes : Read et Write.

3. À quoi ça ressemble dans "notre" appli

Dans nos exemples précédents, on bossait sur une appli bancaire simple pour gérer des comptes. Maintenant, imaginons : on a besoin d’une classe universelle pour un document qu’on peut imprimer (IPrintable), sauvegarder dans un fichier (ISavable) et, pourquoi pas, envoyer par e-mail (IEmailable).

Définissons les interfaces :


public interface IPrintable
{
    void Print();
}

public interface ISavable
{
    void Save(string filePath);
}

public interface IEmailable
{
    void Email(string toAddress);
}

Une classe qui fait tout d’un coup :


public class Statement : IPrintable, ISavable, IEmailable
{
    public string Content { get; set; }

    public void Print()
    {
        Console.WriteLine("J’imprime le relevé...");
        Console.WriteLine(Content);
    }

    public void Save(string filePath)
    {
        // On utilise File.WriteAllText de la lib standard.
        File.WriteAllText(filePath, Content);
        Console.WriteLine($"Relevé sauvegardé dans le fichier : {filePath}");
    }

    public void Email(string toAddress)
    {
        Console.WriteLine($"Relevé envoyé par mail à : {toAddress} (simulation)");
    }
}

Maintenant tu peux utiliser cette classe comme tu veux dans ton code :


var stat = new Statement { Content = "Opérations du mois : +1000 u., -500 d." };
stat.Print();
stat.Save("statement.txt");
stat.Email("boss@bank.corp");

Au fait, dans la vraie vie c’est super pratique de passer un tel objet à des méthodes qui attendent une interface précise, sans se soucier de ses autres capacités. Par exemple, une méthode d’impression peut prendre un paramètre IPrintable et ne rien savoir du reste — genre si c’est aussi une "sauvegarde" ou un "mail".

4. Utiliser des références d’interface : où "voir" ce que mon objet sait faire ?

C’est là que la magie commence. Quand t’as implémenté plusieurs interfaces, tu peux accéder à l’objet seulement comme à un de ses contrats. Par exemple :


IPrintable printable = new Statement { Content = "Conf sur l’abstraction" };
printable.Print(); // Tu peux juste imprimer

// printable.Save("file.txt"); // Erreur : l’interface IPrintable ne connaît pas Save.

Mais si tu switches sur une autre interface :


ISavable savable = printable as ISavable;
if (savable != null)
{
    savable.Save("file.txt");
}

C’est pratique quand tu passes un objet à une méthode qui prend une référence sur une interface précise. Ce genre d’approche réduit le couplage, et ton code devient super flexible : tu peux ajouter de nouvelles implémentations sans toucher à l’ancien code.

5. Implémentation multiple et méthodes identiques dans différentes interfaces

Et là, ça devient fun ! Et si deux interfaces exigent une méthode du même nom, mais avec un sens différent ? Par exemple, imaginons des interfaces pour une machine à café :


public interface IStartable
{
    void Start();
}

public interface IRunnable
{
    void Start();
}

La machine à café peut être "démarrable" (IStartable — lance la préparation de la boisson), et aussi "runnable" (IRunnable — elle se met en mode travail général).

Implémentation par défaut (classique) :


public class CoffeeMachine : IStartable, IRunnable
{
    public void Start()
    {
        Console.WriteLine("La machine à café démarre pour les deux rôles !");
    }
}

Ici, une seule implémentation de Start() "couvre" les deux interfaces.

Et si tu veux des sens différents ?

Tu peux utiliser l’implémentation explicite d’interface :


public class CoffeeMachine : IStartable, IRunnable
{
    void IStartable.Start()
    {
        Console.WriteLine("Démarrage : préparation de la boisson lancée !");
    }

    void IRunnable.Start()
    {
        Console.WriteLine("Démarrage : la machine passe en mode travail.");
    }
}

Dans ce cas, tu peux appeler l’implémentation spécifique seulement via une variable d’interface :


CoffeeMachine cm = new CoffeeMachine();

IStartable startable = cm;
startable.Start(); // Démarrage : préparation de la boisson lancée !

IRunnable runnable = cm;
runnable.Start(); // Démarrage : la machine passe en mode travail.

// cm.Start(); // Ça compile pas ! Start n’est pas accessible comme méthode de la classe.

Au fait, ce genre d’astuce est souvent utilisé dans la lib standard .NET — par exemple, quand une classe implémente plusieurs interfaces similaires de différents frameworks, et que chacune demande son propre comportement.

6. Différence entre implémenter plusieurs interfaces et hériter

Possibilité Classe (héritage) Interfaces
Nombre de types de base Un seul Autant que tu veux
Héritage de code Oui (tu peux avoir une implémentation de base) Non, juste les signatures (sauf méthodes par défaut)
Stockage d’état Oui Non
Ajouter un nouveau rôle Non (ou compliqué, via composition) Facile, tu implémentes l’interface
Mieux pour… Hiérarchies "physiques" Rôles/capacités logiques

7. Application pratique : objets "hybrides"

Grâce à l’implémentation multiple d’interfaces, tu peux faire des classes avec un mix unique de "rôles", sans te prendre la tête avec l’héritage inutile.

Par exemple, dans notre appli bancaire — tu peux faire une classe qui sait stocker des infos sur elle-même, se valider, et imprimer — tout ça via différentes interfaces :


public interface IValidatable
{
    bool Validate();
}

public class Check : IPrintable, ISavable, IValidatable
{
    public string Data { get; set; }

    public void Print()
    {
        Console.WriteLine("Impression du chèque : " + Data);
    }

    public void Save(string filePath)
    {
        File.WriteAllText(filePath, Data);
        Console.WriteLine("Chèque sauvegardé : " + filePath);
    }

    public bool Validate()
    {
        return !string.IsNullOrEmpty(Data);
    }
}

Cette approche permet de construire des architectures super extensibles, où les classes combinent des rôles selon les besoins. Si tu veux ajouter une nouvelle "responsabilité" — t’as juste à implémenter une nouvelle interface.

8. Erreurs classiques et pièges

Erreur fréquente des débutants : oublier d’implémenter tous les membres de l’interface. Le compilateur va pas te rater — il va balancer une erreur et te dire ce qui manque. C’est facile à repérer : "La classe doit implémenter le membre d’interface".

Deuxième souci courant — confusion sur l’accessibilité des méthodes. Si t’as implémenté une méthode explicitement, elle est inaccessible via une variable de la classe — seulement via l’interface.

Encore une situation typique — le refactoring : tu modifies une interface (genre t’ajoutes une méthode), mais t’as pas mis à jour toutes les implémentations. Résultat : erreurs de compilation. Donc, quand tu conçois, évite de changer les interfaces après que plein de classes les utilisent déjà.

2
Mission
C# SELF, niveau 23, leçon 2
Bloqué
Implémentation d'une classe simple pour plusieurs interfaces
Implémentation d'une classe simple pour plusieurs interfaces
2
Mission
C# SELF, niveau 23, leçon 2
Bloqué
Utilisation des références d'interface
Utilisation des références d'interface
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION