Das Interface-Konzept
Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 40 von 61.
Bisher haben unsere Structs Daten und Verhalten miteinander vermischt — zum Beispiel einen Namen zusammen mit einem Funktionszeiger. Aber was wäre, wenn ein Struct nur Funktionszeiger enthalten würde? Dies schafft etwas Mächtiges: ein Interface.
Ein Interface definiert, welche Operationen verfügbar sind, ohne festzulegen, wie sie funktionieren. Es ist ein Vertrag, der besagt: „Jedes Objekt, das dieses Interface implementiert, muss diese Funktionen bereitstellen.“
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Die ILogger-Struktur enthält nichts außer einem Funktionszeiger. Sie besitzt keine eigenen Daten – sie beschreibt rein das Verhalten. Das Präfix „I“ ist eine gängige Konvention, die darauf hinweist, dass es sich um ein Interface handelt.
Jeglicher Code, der ein ILogger akzeptiert, kümmert sich nicht darum, wohin die Log-Nachricht geht. Er könnte auf der Konsole ausgeben, in eine Datei schreiben oder Daten über ein Netzwerk senden. Der Aufrufer weiß lediglich, dass er log aufrufen kann:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // Wir wissen nicht, WIE es protokolliert
}Diese Trennung ist das Wesen des Polymorphismus. Die process-Funktion arbeitet mit jeder Logger-Implementierung zusammen – Sie können Verhaltensweisen austauschen, ohne den Code der Funktion zu ändern. In der nächsten Lektion werden Sie konkrete Implementierungen erstellen, die diesen Interface-Vertrag erfüllen.
Aufgabe
EinfachLassen Sie uns ein ICalculator-Interface bauen — ein Struct, das nur Funktionszeiger enthält und definiert, welche Operationen jeder Taschenrechner unterstützen muss, ohne festzulegen, wie diese funktionieren.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
calculator.h: Definieren Sie hier Ihr Interface. Erstellen Sie einen Funktionszeigertyp namensBinaryOp, der zweiint-Parameter entgegennimmt und einintzurückgibt. Definieren Sie dann einICalculator-Struct, das nur zwei Funktionszeiger enthält:operate(vom TypBinaryOp) unddescribe(ein Funktionszeiger, der keine Parameter entgegennimmt und nichts zurückgibt). Dieses Struct ist ein reines Interface — es enthält keine Daten, sondern nur Verhalten.main.c: Binden Sie Ihren Header ein und implementieren Sie konkrete Funktionen, die das Interface erfüllen können:add— gibt die Summe von zwei Ganzzahlen zurückmultiply— gibt das Produkt von zwei Ganzzahlen zurückdescribe_add— gibtAdditionausdescribe_multiply— gibtMultiplicationaus
ICalculator-Instanz, verknüpfen Sie diese basierend auf der Eingabe und verwenden Sie sie, um eine Berechnung durchzuführen.
Ihr Programm erhält drei Eingaben: zwei Ganzzahlen (integers) und einen Operationscode (1 für Addition, 2 für Multiplikation).
Verknüpfen Sie Ihren ICalculator basierend auf dem Operationscode mit dem entsprechenden Funktionspaar. Rufen Sie dann describe über das Interface auf, um den Namen der Operation auszugeben, gefolgt vom Aufruf von operate, um das Ergebnis zu berechnen und auszugeben.
Beispielhafte Ausgabe, wenn die Eingaben 5, 3 und 1 sind:
Addition
8Beispielhafte Ausgabe, wenn die Eingaben 4, 7 und 2 sind:
Multiplication
28Die entscheidende Erkenntnis ist, dass Ihr ICalculator-Struct einen Vertrag definiert — jeder Code, der es verwendet, weiß, dass er describe und operate aufrufen kann, ohne zu wissen, welche spezifischen Funktionen verknüpft sind. Denken Sie daran, Include-Guards in Ihrer Header-Datei zu verwenden.
Spickzettel
Ein Interface ist ein Struct, das nur Funktionszeiger enthält und definiert, welche Operationen verfügbar sind, ohne festzulegen, wie sie funktionieren.
Definieren eines Interfaces:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Das Präfix „I“ ist eine gängige Konvention, die darauf hinweist, dass es sich um ein Interface handelt. Das Struct enthält keine Daten – es beschreibt rein das Verhalten.
Verwenden eines Interfaces:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // Wir wissen nicht, WIE es loggt
}Diese Trennung ermöglicht Polymorphismus – die Funktion arbeitet mit jeder Implementierung des Interfaces zusammen, was es ermöglicht, Verhaltensweisen auszutauschen, ohne den Code der Funktion zu ändern.
Probier es selbst
#include <stdio.h>
#include "calculator.h"
// TODO: Implementiere die add-Funktion
// Gibt die Summe von zwei Ganzzahlen zurück
// TODO: Implementiere die multiply-Funktion
// Gibt das Produkt von zwei Ganzzahlen zurück
// TODO: Implementiere die describe_add-Funktion
// Gibt "Addition" aus
// TODO: Implementiere die describe_multiply-Funktion
// Gibt "Multiplication" aus
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: Erstelle eine ICalculator-Instanz
// TODO: Basierend auf op_code (1 für add, 2 für multiply),
// verbinde den Taschenrechner mit den entsprechenden Funktionen
// TODO: Rufe describe über das Interface auf
// TODO: Rufe operate über das Interface auf und gib das Ergebnis aus
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