CodeGym /Cours /C# SELF /Utilisation des interfaces en pratique

Utilisation des interfaces en pratique

C# SELF
Niveau 24, Leçon 4
Disponible

1. Interfaces dans la logique métier : boutons et actions

C'est la dernière conférence sur les interfaces, donc il y aura plein d'exemples pratiques pour que tu comprennes mieux comment les interfaces sont utilisées dans la vraie vie.

Les interfaces sont particulièrement précieuses dans les grosses applis. Donc ci-dessous, tu trouveras des cas un peu plus complexes, parfois en avance sur le programme du cours. Si ça t'intéresse — top ! Sinon — passe au niveau suivant :P

L'exemple le plus simple — boutons et clics dessus

On continue à développer notre appli console d'apprentissage — imagine qu'on a un système simple d'éléments utilisateur (genre boutons, champs texte et switches) dans un menu console.

On veut que certains éléments réagissent au clic (boutons), et d'autres non (genre un titre statique). Les interfaces permettent de gérer ça de façon élégante.

Étape 1 : On crée une interface


// Composant sur lequel on peut "cliquer" :
public interface IClickable
{
    void Click();
}

Étape 2 : On applique l'interface à différents types d'éléments


// Classe de base pour tous les éléments du menu
public class MenuItem
{
    public string Title { get; set; }

    public MenuItem(string title)
    {
        Title = title;
    }

    public virtual void Display()
    {
        Console.WriteLine(Title);
    }
}

// Bouton : on peut "cliquer" dessus
public class Button : MenuItem, IClickable
{
    public Button(string title) : base(title) {}

    public void Click()
    {
        Console.WriteLine($"[Bouton] {Title} a été cliqué !");
    }
}

// Juste un label : pas cliquable
public class Label : MenuItem
{
    public Label(string title) : base(title) {}
}

Étape 3 : On utilise le polymorphisme de l'interface


IClickable[] clickableItems = new IClickable[]
{
    new Button("Enregistrer"),
    new Button("Quitter")
    // Label ne peut pas être ajouté ici — il n'est pas IClickable !
};

foreach (var item in clickableItems)
{
    item.Click();
}

Tu vois la magie ? La liste accepte seulement ceux qui savent faire Click, donc tu n'auras pas d'erreur d'exécution si tu essaies de "cliquer" sur un objet inadapté.

2. Interfaces et pattern “Stratégie” : choisir un algorithme à la volée

Supposons que tu écris une appli où tu peux sauvegarder des rapports de différentes façons : dans un fichier, une base de données, dans le “cloud”, ou même dans Telegram à ton boss (ne demande pas, tout arrive).

Étape 1 : On formule le problème

Il faut que la classe ReportGenerator puisse bosser avec n'importe quelle méthode de sauvegarde, sans connaître les détails d'implémentation.

Étape 2 : On décrit l'interface-stratégie


public interface IDataSaver
{
    void Save(string reportData);
}

Étape 3 : On implémente différentes variantes


public class FileDataSaver : IDataSaver
{
    public void Save(string reportData)
    {
        Console.WriteLine("[FileDataSaver] J'enregistre dans un fichier...\n" + reportData);
        // Ici il pourrait y avoir du code pour File.WriteAllText(...)
    }
}

public class DatabaseDataSaver : IDataSaver
{
    public void Save(string reportData)
    {
        Console.WriteLine("[DatabaseDataSaver] J'enregistre dans la base de données...\n" + reportData);
        // Ici il pourrait y avoir du code pour écrire dans la base
    }
}

Étape 4 : On utilise l'interface pour changer la stratégie de sauvegarde


public class ReportGenerator
{
    private readonly IDataSaver _dataSaver;

    public ReportGenerator(IDataSaver dataSaver)
    {
        _dataSaver = dataSaver;
    }

    public void GenerateReport()
    {
        string report = "C'est un rapport important !";
        Console.WriteLine("On génère le rapport...");
        _dataSaver.Save(report);
    }
}

Démonstration de la flexibilité


// On peut changer la méthode de sauvegarde sans réécrire ReportGenerator !
IDataSaver fileSaver = new FileDataSaver();
ReportGenerator fileReport = new ReportGenerator(fileSaver);
fileReport.GenerateReport();

IDataSaver dbSaver = new DatabaseDataSaver();
ReportGenerator dbReport = new ReportGenerator(dbSaver);
dbReport.GenerateReport();

En pratique : Si demain tu veux écrire les rapports dans un nouveau service super moderne, il te suffit d'écrire une nouvelle classe qui implémente l'interface, et de brancher l'objet dans ton archi, sans toucher une ligne du code existant.

3. Interfaces dans .NET : IDisposable et using

Un des interfaces les plus répandues dans .NET — IDisposable. Elle est implémentée par toutes les classes qui bossent avec des ressources non managées : fichiers, streams, connexions réseau.

Pourquoi on a besoin de IDisposable ?

Quand tu bosses avec une ressource qu'il faut absolument libérer (genre fermer un fichier), tu implémentes IDisposable et tu écris la méthode Dispose. Ça permet de passer ces objets à l'opérateur using, qui garantit l'appel de Dispose à la sortie du bloc.

Exemple : Simulation de travail avec un fichier

public class FakeFile : IDisposable
{
    public string FileName { get; }

    public FakeFile(string fileName)
    {
        FileName = fileName;
        Console.WriteLine($"Fichier ouvert : {fileName}");
    }

    public void Dispose()
    {
        Console.WriteLine($"Fichier fermé : {FileName}");
    }
}

// Dans le programme principal :
using (var file = new FakeFile("rapport.txt"))
{
    Console.WriteLine("On écrit dans le fichier...");
    // le fichier sera "fermé" automatiquement après using
}

Sortie :

Fichier ouvert : rapport.txt
On écrit dans le fichier...
Fichier fermé : rapport.txt

Vraiment utile : tu n'oublieras pas de fermer le fichier — l'interface et l'infra du langage te protègent.

4. Interfaces dans les collections et LINQ

Quand tu bosses avec des listes, tableaux, dictionnaires, tu utilises déjà des interfaces, même si tu ne t'en rends pas compte.


List<int> list = new List<int> { 1, 2, 3 };
IEnumerable<int> enumerable = list; // tout roule !

// Maintenant tu peux parcourir les éléments comme ça :
foreach(var x in enumerable)
{
    Console.WriteLine(x);
}

La plupart des méthodes LINQ bossent avec les collections via l'interface IEnumerable<T>. Ça permet d'écrire du code qui ne dépend pas du type concret de la collection.

Pourquoi c'est cool ?
Tu peux remplacer List<T> par T[], HashSet<T> ou même ta propre collection — et ton code continuera à marcher !

5. Interfaces pour les tests (objets Mock)

Dans les tests, c'est super important d'isoler le code des dépendances externes : pas toucher la vraie base, pas toucher les vrais fichiers. Les interfaces permettent d'utiliser des mocks et de tester le code sans toucher aux vraies bases ou fichiers.


public class FakeDataSaver : IDataSaver
{
    public bool WasCalled { get; private set; } = false;
    public void Save(string reportData)
    {
        WasCalled = true;
        Console.WriteLine("Données sauvegardées dans le mock !");
    }
}

// Dans le test
FakeDataSaver saver = new FakeDataSaver();
ReportGenerator generator = new ReportGenerator(saver);
generator.GenerateReport();

Console.WriteLine($"Save a été appelé ? {saver.WasCalled}");

Résultat : le test ne dépend pas du monde réel, mais vérifie que la méthode voulue a bien été appelée !

6. Implémentation explicite d'interfaces pour résoudre les conflits

Parfois une classe doit implémenter deux interfaces avec les mêmes méthodes, mais la logique de ces méthodes est différente.


public interface IFlyable
{
    void Move();
}

public interface ISwimmable
{
    void Move();
}

public class Duck : IFlyable, ISwimmable
{
    // Implémentation explicite
    void IFlyable.Move()
    {
        Console.WriteLine("Le canard vole !");
    }

    void ISwimmable.Move()
    {
        Console.WriteLine("Le canard nage !");
    }
}

Duck duck = new Duck();

// duck.Move(); // Erreur : pas de telle méthode !
((IFlyable)duck).Move(); // "Le canard vole !"
((ISwimmable)duck).Move(); // "Le canard nage !"

Ça peut paraître hardcore, mais parfois c'est exactement ce que demandent les specs !

7. Interfaces dans le modèle événementiel : INotifyPropertyChanged

Dans .NET, il y a des interfaces standard pour gérer les événements — par exemple, quand des données changent dans les modèles et que l'UI doit le savoir (très courant en WPF, MAUI et autres GUI).


using System.ComponentModel;

public class Person : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get => name;
        set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
            }
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
}

L'idée : tout framework qui supporte le data binding attend que ta classe implémente cette interface — et alors l'UI se mettra à jour automatiquement quand les propriétés changent.

8. Interfaces dans le pattern “Factory”

Les interfaces sont parfaites pour construire des factories — des classes qui créent différents objets interchangeables.


public interface ITransport
{
    void Move();
}

public class Bicycle : ITransport
{
    public void Move() => Console.WriteLine("On roule à vélo !");
}

public class Car : ITransport
{
    public void Move() => Console.WriteLine("On roule en voiture !");
}

public class TransportFactory
{
    public static ITransport Create(string type)
    {
        return type switch
        {
            "bike" => new Bicycle(),
            "car" => new Car(),
            _ => throw new ArgumentException("Type de transport inconnu")
        };
    }
}

ITransport transport = TransportFactory.Create("bike");
transport.Move(); // "On roule à vélo !"

9. Interfaces pour les événements : exemple d'événement custom

Décrivons une interface “listener d'événement” :


public interface ILoginListener
{
    void OnLogin(string userName);
}

// Classe qui déclenche l'événement
public class LoginManager
{
    private List<ILoginListener> listeners = new();

    public void Subscribe(ILoginListener listener) => listeners.Add(listener);

    public void Login(string userName)
    {
        Console.WriteLine($"Utilisateur {userName} est connecté.");
        foreach (var listener in listeners)
            listener.OnLogin(userName);
    }
}

// Classe qui implémente l'interface
public class WelcomeMessage : ILoginListener
{
    public void OnLogin(string userName)
    {
        Console.WriteLine($"Bienvenue, {userName} !");
    }
}

LoginManager manager = new();
manager.Subscribe(new WelcomeMessage());
manager.Login("User");
// Utilisateur User est connecté.
// Bienvenue, User !

En entretien : si on te demande — “comment tu ferais ton propre système d'événements ?”, parle sans hésiter des interfaces listeners !

10. Interfaces pour les plugins (extensibilité des applis)

Beaucoup de grosses applis supportent les plugins. Grâce aux interfaces, ton appli peut “charger” de nouveaux modules à la volée, sans connaître leur intérieur.


public interface IPlugin
{
    string Name { get; }
    void Run();
}

// Ton appli :
public class PluginLoader
{
    public void LoadAndRun(IEnumerable<IPlugin> plugins)
    {
        foreach (var plugin in plugins)
        {
            Console.WriteLine($"Je lance le plugin : {plugin.Name}");
            plugin.Run();
        }
    }
}

Les plugins peuvent être développés par des tiers — le principal, c'est qu'ils implémentent l'interface. Ton appli devient extensible !

2
Mission
C# SELF, niveau 24, leçon 4
Bloqué
Implémentation d'une interface pour des figures géométriques
Implémentation d'une interface pour des figures géométriques
2
Mission
C# SELF, niveau 24, leçon 4
Bloqué
Une simple fabrique utilisant des interfaces
Une simple fabrique utilisant des interfaces
1
Étude/Quiz
Utilisation des interfaces, niveau 24, leçon 4
Indisponible
Utilisation des interfaces
Interfaces avancées
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION