Interfaces implementieren
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 41 von 61.
Nachdem du nun verstanden hast, was eine Schnittstelle ist – eine Struktur, die ausschließlich Funktionszeiger enthält –, ist es an der Zeit, konkrete Implementierungen zu erstellen, die diesen Vertrag erfüllen.
Erinnern wir uns an unsere ILogger-Schnittstelle aus der vorherigen Lektion:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Um diese Schnittstelle zu implementieren, schreiben wir tatsächliche Funktionen, die der Signatur von LogFunc entsprechen, und erstellen anschließend ILogger-Instanzen, denen diese Funktionen zugewiesen werden:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message); // Simuliere Dateiausgabe
}
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}Jede „Konstruktor“-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 Protokollierungsimplementierung entkoppelt. Du kannst Protokollierer frei austauschen, ohne die Kernlogik zu ändern: Das ist die Stärke der Programmierung gegen eine Schnittstelle.
Aufgabe
EinfachErstellen wir ein INotifier-System, das veranschaulicht, wie verschiedene Implementierungen denselben Schnittstellenvertrag erfüllen können. Du wirst konkrete Benachrichtigungsdienste erstellen, einen EmailNotifier und einen SMSNotifier, die beide einer gemeinsamen Schnittstelle entsprechen, aber unterschiedliche Ausgaben erzeugen.
Du wirst deinen Code auf drei Dateien verteilen:
notifier.h: Definiere hier die Schnittstelle. Erstelle einen Funktionszeigertyp namensNotifyFunc, der eineconst char*-Nachricht übernimmt und nichts zurückgibt. Definiere anschließend eineINotifier-Struktur, die ausschließlich einennotify-Funktionszeiger dieses Typs enthält. Deklariere außerdem zwei Konstruktorfunktionen:create_email_notifierundcreate_sms_notifier, die jeweils einenINotifierals Wert zurückgeben.notifier.c: Implementiere hier die konkreten Benachrichtigungsfunktionen und Konstruktoren. Erstelle:email_notify: Gibt die Nachricht im Format[EMAIL] messageaussms_notify: Gibt die Nachricht im Format[SMS] messageauscreate_email_notifier: Gibt einen mitINotifierverknüpftenemail_notifyzurückcreate_sms_notifier: Gibt einen mitINotifierverknüpftensms_notifyzurück
main.c: Führe hier alles zusammen. Lies einen Benachrichtigungstyp und eine Nachricht ein, erstelle mithilfe der Konstruktorfunktionen den passenden Benachrichtigungsdienst und sende die Benachrichtigung über die Schnittstelle.
Dein Programm erhält zwei Eingaben: einen Benachrichtigungstyp (1 für E-Mail, 2 für SMS) und eine zu sendende Nachricht.
Verwende die Konstruktorfunktionen, um den passenden Benachrichtigungsdienst zu erstellen, und rufe anschließend notify über die Schnittstelle auf, um die Nachricht anzuzeigen.
Beispielausgabe, wenn der Typ 1 und die Nachricht Meeting at 3pm lautet:
[EMAIL] Meeting at 3pmBeispielausgabe, wenn der Typ 2 und die Nachricht Your code shipped lautet:
[SMS] Your code shippedDie Stärke dieses Musters besteht darin, dass main.c nicht wissen muss, wie die einzelnen Benachrichtigungsdienste intern funktionieren. Es ruft einfach die notify-Funktion der Schnittstelle auf, und die richtige Implementierung wird ausgeführt. Denke daran, Include-Guards in deiner Headerdatei zu verwenden.
Probier es selbst
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// Lies den Benachrichtigungstyp (1 für E-Mail, 2 für SMS)
scanf("%d", &type);
getchar(); // Zeilenumbruch verbrauchen
// Lies die Nachricht
fgets(message, sizeof(message), stdin);
// Entferne 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 die Schnittstelle 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 Objektorientierte Programmierung
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-ImplementierungRechteck-ImplementierungPolymorphe VerwendungShape-ContainerÜbe selbstständig: Online-C-Compiler