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.
Défi
FacileConstruisons 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éNotifyFuncqui prend un messageconst char*et ne renvoie rien. Définissez ensuite une structureINotifiercontenant uniquement un pointeur de fonctionnotifyde ce type. Déclarez également deux fonctions constructeurs :create_email_notifieretcreate_sms_notifier, chacune retournant unINotifierpar 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] messagesms_notify— affiche le message au format :[SMS] messagecreate_email_notifier— retourne unINotifierlié àemail_notifycreate_sms_notifier— retourne unINotifierlié à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 3pmExemple de sortie quand le type est 2 et le message est Your code shipped :
[SMS] Your code shippedLa 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;
}
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
1Bases de la programmation modulaire
Fichiers d'en-têteGardes d'inclusionFichiers sourcesFonctions statiquesRécapitulatif : Calculatrice modulaire4Encapsulation
Concept des pointeurs opaquesDéfinir des structures opaquesGetters et settersValidation dans les settersRécapitulatif : La boîte secrète2Objets et méthodes
Structs comme objetsLe pointeur 'Self'Rigueur du mot-clé constPointeur vs ValeurMéthodes utilitairesRécapitulatif : Point Manager5Projet : Compte bancaire simple
Configuration du projetImplémentation du compte8Polymorphisme
Pointeurs de fonctions dans les structuresSimulation de méthodesLe concept d'interfaceImplémentation d'interfacesItération polymorphiqueRécapitulatif : Greeter11Patrons de conception en C
Patron SingletonPatron FabriquePatron ItérateurRécapitulatif : Logger Factory3Cycle de vie des objets
Pattern de constructeurPattern de destructeurInitialisation sur la pileCopie profondeRécapitulatif : String Wrapper6Héritage par composition
Imbrication de structLa règle du premier membreAccès aux membres parentsUpcastingRécapitulatif : Hiérarchie des formes9Projet : Dessinateur de formes
Aperçu du projetImplémentation du cercle