Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 namens NotifyFunc, der eine const char* Nachricht entgegennimmt und nichts zurückgibt. Definieren Sie dann ein INotifier-Struct, das nur einen notify-Funktionszeiger dieses Typs enthält. Deklarieren Sie außerdem zwei Konstruktorfunktionen: create_email_notifier und create_sms_notifier, die jeweils einen INotifier als Wert zurückgeben.
  • notifier.c: Implementieren Sie hier die konkreten Benachrichtigungsfunktionen und Konstruktoren. Erstellen Sie:
    • email_notify — gibt die Nachricht im Format: [EMAIL] message aus
    • sms_notify — gibt die Nachricht im Format: [SMS] message aus
    • create_email_notifier — gibt einen INotifier zurück, der mit email_notify verknüpft ist
    • create_sms_notifier — gibt einen INotifier zurück, der mit sms_notify verknü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 3pm

Beispielausgabe, wenn der Typ 2 und die Nachricht Your code shipped ist:

[SMS] Your code shipped

Die 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;
}
quiz iconTeste dich selbst

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