Aller au contenu principal

Interfaces

Notions théoriques

Qu'est-ce qu'une interface ?

Une interface définit un contrat : elle liste des méthodes et propriétés que les classes doivent implémenter. Contrairement à une classe abstraite, une interface ne contient (généralement) que des déclarations, pas d'implémentation.

interface IDescriptible // convention : nom commence par I
{
string Decrire(); // déclaration — pas de corps
}

class Produit : IDescriptible
{
public string Nom { get; set; } = "";
public decimal Prix { get; set; }

public string Decrire() // obligatoire — le contrat l'impose
=> $"{Nom} ({Prix:F2} €)";
}

IDescriptible p = new Produit { Nom = "Clavier", Prix = 49.99m };
Console.WriteLine(p.Decrire());

Convention de nommage

Les interfaces commencent par I (majuscule) : IComparable, IDisposable, IFormattable. C'est une convention universelle en .NET.

Implémenter plusieurs interfaces

Contrairement à l'héritage de classe (une seule parente), une classe peut implémenter plusieurs interfaces :

interface ISauvegardable
{
string Serialiser();
}

interface IValidable
{
bool EstValide();
string[] ObtenirErreurs();
}

class Etudiant : ISauvegardable, IValidable
{
public string Nom { get; set; } = "";
public double Note { get; set; }

public string Serialiser() => $"{Nom},{Note}";

public bool EstValide()
=> !string.IsNullOrWhiteSpace(Nom) && Note >= 0 && Note <= 20;

public string[] ObtenirErreurs()
{
var erreurs = new List<string>();
if (string.IsNullOrWhiteSpace(Nom)) erreurs.Add("Le nom est requis.");
if (Note < 0 || Note > 20) erreurs.Add($"Note {Note} invalide (0-20).");
return [.. erreurs];
}
}

Interface + héritage

Une classe peut hériter d'une classe parente et implémenter des interfaces :

class Produit : Article, IDescriptible, ISauvegardable
{
// hérite de Article, doit implémenter IDescriptible et ISauvegardable
}

Interfaces qui héritent d'interfaces

interface ILisable { string Lire(); }

interface IModifiable : ILisable // exige aussi Lire() (hérité)
{
void Ecrire(string contenu);
void Effacer();
}

Méthodes par défaut (C# 8+)

Depuis C# 8, une interface peut avoir des méthodes avec implémentation par défaut :

interface IAffichable
{
string Decrire();
void Afficher() => Console.WriteLine(Decrire()); // méthode par défaut
}

Classe abstraite vs interface

Classe abstraiteInterface
Implémentation partielleOuiNon (sauf défaut C# 8+)
ChampsOuiNon
ConstructeurOuiNon
Héritage multipleNon (une seule parente)Oui (plusieurs interfaces)
Usage typiquePartager du code entre classes liéesContrat comportemental pour classes non liées

Exemple pratique

interface IExportable
{
string ExporterCsv();
string ExporterJson();
}

interface IValidable
{
bool EstValide();
}

class Commande : IExportable, IValidable
{
public int Id { get; }
public string Client { get; set; } = "";
public decimal Montant { get; set; }
public string Statut { get; set; } = "En attente";

private static int _prochainId = 1;
public Commande() { Id = _prochainId++; }

public bool EstValide()
=> !string.IsNullOrWhiteSpace(Client) && Montant > 0;

public string ExporterCsv()
=> $"{Id},{Client},{Montant:F2},{Statut}";

public string ExporterJson()
=> $"{{\"id\":{Id},\"client\":\"{Client}\",\"montant\":{Montant:F2},\"statut\":\"{Statut}\"}}";
}

var commandes = new List<Commande>
{
new Commande { Client = "Alice", Montant = 149.99m, Statut = "Payée" },
new Commande { Client = "", Montant = 50m }, // invalide
new Commande { Client = "Bob", Montant = 89.50m, Statut = "Expédiée" },
};

Console.WriteLine("=== Export CSV ===");
Console.WriteLine("id,client,montant,statut");
foreach (IExportable c in commandes)
Console.WriteLine(c.ExporterCsv());

Console.WriteLine("\n=== Validation ===");
foreach (Commande c in commandes)
Console.WriteLine($"Commande #{c.Id} ({c.Client}) : {(c.EstValide() ? "valide" : "INVALIDE")}");

Test de mémorisation/compréhension


Quelle est la convention de nommage des interfaces en C# ?


Une classe peut-elle implémenter plusieurs interfaces ?


Quelle est la différence fondamentale entre une interface et une classe abstraite ?


Quelle fonctionnalité C# 8 a-t-on ajoutée aux interfaces ?


Comment déclare-t-on qu'une classe hérite de `Personne` ET implémente `IComparable` ?


TP pour réfléchir et résoudre des problèmes

Vous allez créer un système de notification avec interfaces.

Étape 1 — Définir l'interface INotification


Bonne pratique - Interface minimaliste

Déclarez dans une interface uniquement ce qui est strictement nécessaire. Préférez plusieurs petites interfaces cohérentes (Interface Segregation Principle — le "I" de SOLID).

Étape 2 — Implémenter plusieurs canaux


Bonne pratique - Même contrat, comportements différents

Les trois classes implémentent le même contrat mais avec des logiques complètement différentes. Le code client qui reçoit une INotification n'a pas besoin de savoir s'il s'agit d'un email ou d'un SMS. C'est le polymorphisme via les interfaces.

Étape 3 — Service utilisant l'interface


Bonne pratique - Dépendre des interfaces, pas des implémentations

NotificateurMultiCanal stocke des INotification, pas des NotificationEmail. On peut lui ajouter un canal webhook ou Slack sans modifier son code. C'est le principe Dependency Inversion (le "D" de SOLID).

📌 Une solution