Interfaces implementieren
Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 41 von 61.
Da Sie nun verstehen, was ein Interface ist – ein Struct, das nur Funktionszeiger enthält – ist es an der Zeit, konkrete Implementierungen zu erstellen, die diesen Vertrag erfüllen.
Erinnern wir uns an unser ILogger-Interface aus der vorherigen Lektion:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Um dieses Interface zu implementieren, schreiben wir tatsächliche Funktionen, die der LogFunc-Signatur entsprechen, und erstellen dann ILogger-Instanzen, denen diese Funktionen zugewiesen sind:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message); // Simuliert die Dateiausgabe
}
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}Jede „Constructor“-Funktion gibt ein ILogger mit einer anderen eingebundenen Funktion zurück. Code, der diese Logger verwendet, muss nicht wissen, welche Implementierung er erhalten hat:
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); // Verwendet console_log
do_work(&file); // Verwendet file_log
return 0;
}Die Funktion do_work ist vollständig von der Logging-Implementierung entkoppelt. Sie können Logger frei austauschen, ohne die Kernlogik zu ändern – das ist die Stärke der Programmierung gegen eine Schnittstelle.
Aufgabe
EinfachLassen Sie uns ein INotifier-System bauen, das demonstriert, wie verschiedene Implementierungen denselben Interface-Vertrag erfüllen können. Sie werden konkrete Notifier erstellen — einen EmailNotifier und einen SMSNotifier —, die beide einem gemeinsamen Interface entsprechen, aber unterschiedliche Ausgaben erzeugen.
Sie werden Ihren Code über drei Dateien organisieren:
notifier.h: Definieren Sie hier das Interface. Erstellen Sie einen Funktionszeigertyp namensNotifyFunc, der eineconst char*Nachricht entgegennimmt und nichts zurückgibt. Definieren Sie dann einINotifier-Struct, das nur einennotify-Funktionszeiger dieses Typs enthält. Deklarieren Sie außerdem zwei Konstruktorfunktionen:create_email_notifierundcreate_sms_notifier, die jeweils einenINotifierals Wert zurückgeben.notifier.c: Implementieren Sie hier die konkreten Benachrichtigungsfunktionen und Konstruktoren. Erstellen Sie:email_notify— gibt die Nachricht im Format:[EMAIL] messageaussms_notify— gibt die Nachricht im Format:[SMS] messageauscreate_email_notifier— gibt einenINotifierzurück, der mitemail_notifyverknüpft istcreate_sms_notifier— gibt einenINotifierzurück, der mitsms_notifyverknüpft ist
main.c: Bringen Sie hier alles zusammen. Lesen Sie einen Benachrichtigungstyp und eine Nachricht ein, erstellen Sie den entsprechenden Notifier mithilfe der Konstruktorfunktionen und senden Sie die Benachrichtigung über das Interface.
Ihr Programm erhält zwei Eingaben: einen Benachrichtigungstyp (1 für E-Mail, 2 für SMS) und eine zu sendende Nachricht.
Verwenden Sie die Konstruktorfunktionen, um den entsprechenden Notifier zu erstellen, und rufen Sie dann notify über das Interface auf, um die Nachricht anzuzeigen.
Beispielausgabe, wenn der Typ 1 und die Nachricht Meeting at 3pm ist:
[EMAIL] Meeting at 3pmBeispielausgabe, wenn der Typ 2 und die Nachricht Your code shipped ist:
[SMS] Your code shippedDie Stärke dieses Musters liegt darin, dass main.c nicht wissen muss, wie jeder Notifier intern funktioniert — es ruft einfach die notify-Funktion des Interfaces auf, und die korrekte Implementierung wird ausgeführt. Denken Sie daran, Include-Guards in Ihrer Header-Datei zu verwenden.
Spickzettel
Um ein Interface in C zu implementieren, erstellen Sie konkrete Funktionen, die der Signatur des Funktionszeigers entsprechen, und weisen Sie diese dann Interface-Instanzen zu.
Definieren Sie das Interface mit Funktionszeigern:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Erstellen Sie konkrete Implementierungen:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message);
}Verwenden Sie Konstruktorfunktionen, um Interface-Instanzen zu erstellen:
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}Code, der das Interface verwendet, ist von spezifischen Implementierungen entkoppelt:
void do_work(ILogger* logger) {
logger->log("Starting work...");
}
int main() {
ILogger console = create_console_logger();
ILogger file = create_file_logger();
do_work(&console); // Verwendet console_log
do_work(&file); // Verwendet file_log
return 0;
}Probier es selbst
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// Lies den Benachrichtigungstyp ein (1 für E-Mail, 2 für SMS)
scanf("%d", &type);
getchar(); // Zeilenumbruch konsumieren
// Lies die Nachricht ein
fgets(message, sizeof(message), stdin);
// Entferne den abschließenden Zeilenumbruch, falls vorhanden
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Erstelle den entsprechenden Notifier basierend auf dem Typ
// Verwende create_email_notifier() für Typ 1
// Verwende create_sms_notifier() für Typ 2
// TODO: Rufe die notify-Funktion über das Interface auf
// notifier.notify(message);
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
1Grundlagen der modularen Programmierung
Header-DateienInclude GuardsQuelldateienStatische FunktionenWiederholung: Modularer Taschenrechner4Kapselung
Konzept der Opaque PointersOpaque Structs definierenGetter und SetterValidierung in SetternRückblick: Die geheime Box2Objekte und Methoden
Structs als ObjekteDer 'Self'-PointerConst-CorrectnessPointer vs. WertHilfsmethodenZusammenfassung: Point Manager5Projekt: Einfaches Bankkonto
Projekt-SetupImplementierung des Kontos8Polymorphismus
Funktionszeiger in StructsMethoden simulierenDas Interface-KonzeptInterfaces implementierenPolymorphe IterationWiederholung: Greeter3Objekt-Lebenszyklus
Konstruktor-MusterDestruktor-MusterStack-InitialisierungTiefe KopieRückblick: String-Wrapper6Vererbung durch Komposition
Struct-EinbettungDie First-Member-RegelZugriff auf Parent-MemberUpcastingRückblick: Formenhierarchie9Projekt: Formen-Zeichner
ProjektübersichtKreis-Implementierung