Menu
Coddy logo textTech

Implémentation d'interfaces

Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 41 sur 61.

Maintenant que vous comprenez ce qu'est une interface — une struct contenant uniquement des pointeurs de fonction — il est temps de créer des implémentations concrètes qui remplissent ce contrat.

Rappelons notre interface ILogger de la leçon précédente :

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

Pour implémenter cette interface, nous écrivons des fonctions réelles qui correspondent à la signature LogFunc, puis nous créons des instances ILogger avec ces fonctions assignées :

void console_log(const char* message) {
    printf("[CONSOLE] %s\n", message);
}

void file_log(const char* message) {
    printf("[FILE] %s\n", message);  // Simulation de la sortie fichier
}

ILogger create_console_logger() {
    ILogger logger = { console_log };
    return logger;
}

ILogger create_file_logger() {
    ILogger logger = { file_log };
    return logger;
}

Chaque fonction « constructeur » renvoie un ILogger avec une fonction différente intégrée. Le code qui utilise ces loggers n'a pas besoin de savoir quelle implémentation il a reçue :

void do_work(ILogger* logger) {
    logger->log("Starting work...");
    logger->log("Work complete!");
}

int main() {
    ILogger console = create_console_logger();
    ILogger file = create_file_logger();
    
    do_work(&console);  // Utilise console_log
    do_work(&file);     // Utilise file_log
    return 0;
}

La fonction do_work est complètement découplée de l'implémentation de la journalisation. Vous pouvez changer de logger librement sans modifier la logique principale — c'est là toute la puissance de la programmation orientée interface.

challenge icon

Défi

Facile

Construisons un système INotifier qui démontre comment différentes implémentations peuvent remplir le même contrat d'interface. Vous allez créer des notificateurs concrets — un EmailNotifier et un SMSNotifier — qui se conforment tous deux à une interface commune mais produisent des sorties différentes.

Vous organiserez votre code sur trois fichiers :

  • notifier.h : Définissez l'interface ici. Créez un type de pointeur de fonction appelé NotifyFunc qui prend un message const char* et ne renvoie rien. Définissez ensuite une structure INotifier contenant uniquement un pointeur de fonction notify de ce type. Déclarez également deux fonctions constructeurs : create_email_notifier et create_sms_notifier, chacune retournant un INotifier par valeur.
  • notifier.c : Implémentez les fonctions de notification concrètes et les constructeurs ici. Créez :
    • email_notify — affiche le message au format : [EMAIL] message
    • sms_notify — affiche le message au format : [SMS] message
    • create_email_notifier — retourne un INotifier lié à email_notify
    • create_sms_notifier — retourne un INotifier lié à sms_notify
  • main.c : Rassemblez tout ici. Lisez un type de notification et un message, créez le notificateur approprié en utilisant les fonctions constructeurs, et envoyez la notification via l'interface.

Votre programme recevra deux entrées : un type de notification (1 pour l'email, 2 pour le SMS) et un message à envoyer.

Utilisez les fonctions constructeurs pour créer le notificateur approprié, puis appelez notify via l'interface pour afficher le message.

Exemple de sortie quand le type est 1 et le message est Meeting at 3pm :

[EMAIL] Meeting at 3pm

Exemple de sortie quand le type est 2 et le message est Your code shipped :

[SMS] Your code shipped

La puissance de ce modèle est que main.c n'a pas besoin de savoir comment chaque notificateur fonctionne en interne — il appelle simplement la fonction notify de l'interface, et l'implémentation correcte s'exécute. N'oubliez pas d'utiliser des gardes d'inclusion dans votre fichier d'en-tête.

Aide-mémoire

Pour implémenter une interface en C, créez des fonctions concrètes qui correspondent à la signature du pointeur de fonction, puis affectez-les à des instances d'interface.

Définissez l'interface avec des pointeurs de fonction :

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

Créez des implémentations concrètes :

void console_log(const char* message) {
    printf("[CONSOLE] %s\n", message);
}

void file_log(const char* message) {
    printf("[FILE] %s\n", message);
}

Utilisez des fonctions constructeurs pour créer des instances d'interface :

ILogger create_console_logger() {
    ILogger logger = { console_log };
    return logger;
}

ILogger create_file_logger() {
    ILogger logger = { file_log };
    return logger;
}

Le code utilisant l'interface est découplé des implémentations spécifiques :

void do_work(ILogger* logger) {
    logger->log("Starting work...");
}

int main() {
    ILogger console = create_console_logger();
    ILogger file = create_file_logger();
    
    do_work(&console);  // Utilise console_log
    do_work(&file);     // Utilise file_log
    return 0;
}

Essayez vous-même

#include <stdio.h>
#include "notifier.h"

int main() {
    int type;
    char message[256];
    
    // Lire le type de notification (1 pour email, 2 pour SMS)
    scanf("%d", &type);
    getchar(); // consommer le saut de ligne
    
    // Lire le message
    fgets(message, sizeof(message), stdin);
    // Supprimer le saut de ligne final s'il est présent
    int len = 0;
    while (message[len] != '\0') len++;
    if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
    
    // TODO: Créer le notificateur approprié en fonction du type
    // Utiliser create_email_notifier() pour le type 1
    // Utiliser create_sms_notifier() pour le type 2
    
    // TODO: Appeler la fonction notify via l'interface
    // notifier.notify(message);
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Object Oriented Programming