Implementowanie interfejsów
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 41 z 61.
Skoro już wiesz, czym jest interfejs — strukturą zawierającą wyłącznie wskaźniki do funkcji — czas utworzyć konkretne implementacje, które spełniają ten kontrakt.
Przypomnij sobie interfejs ILogger z poprzedniej lekcji:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Aby zaimplementować ten interfejs, piszemy rzeczywiste funkcje zgodne z sygnaturą LogFunc, a następnie tworzymy instancje ILogger z przypisanymi do nich funkcjami:
void console_log(const char* message) {
printf("[CONSOLE] %s\n", message);
}
void file_log(const char* message) {
printf("[FILE] %s\n", message); // Symulowanie wyjścia do pliku
}
ILogger create_console_logger() {
ILogger logger = { console_log };
return logger;
}
ILogger create_file_logger() {
ILogger logger = { file_log };
return logger;
}Każda funkcja „konstruktora” zwraca ILogger z podłączoną inną funkcją. Kod korzystający z tych rejestratorów nie musi wiedzieć, którą implementację otrzymał:
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); // Używa console_log
do_work(&file); // Używa file_log
return 0;
}Funkcja do_work jest całkowicie odseparowana od implementacji logowania. Możesz swobodnie zmieniać rejestratory bez modyfikowania jakiejkolwiek logiki podstawowej: na tym polega siła programowania zorientowanego na interfejs.
Wyzwanie
ŁatwyZbudujmy system INotifier, który pokazuje, jak różne implementacje mogą spełniać ten sam kontrakt interfejsu. Utworzysz konkretne powiadamiacze: EmailNotifier i SMSNotifier, które będą zgodne ze wspólnym interfejsem, ale będą generować różne wyniki.
Podzielisz kod na trzy pliki:
notifier.h: Zdefiniuj tutaj interfejs. Utwórz typ wskaźnika na funkcję o nazwieNotifyFunc, która przyjmuje wiadomość typuconst char*i nic nie zwraca. Następnie zdefiniuj strukturęINotifier, zawierającą wyłącznie wskaźnik na funkcjęnotifytego typu. Zadeklaruj też dwie funkcje konstruktora:create_email_notifiericreate_sms_notifier, z których każda zwraca wartość typuINotifier.notifier.c: Zaimplementuj tutaj konkretne funkcje powiadomień i konstruktory. Utwórz:email_notify: wyświetla wiadomość w formacie:[EMAIL] messagesms_notify: wyświetla wiadomość w formacie:[SMS] messagecreate_email_notifier: zwracaINotifierpowiązany zemail_notifycreate_sms_notifier: zwracaINotifierpowiązany zsms_notify
main.c: Połącz tutaj wszystkie elementy. Odczytaj typ powiadomienia i wiadomość, utwórz odpowiedni powiadamiacz za pomocą funkcji konstruktora i wyślij powiadomienie przez interfejs.
Program otrzyma dwa dane wejściowe: typ powiadomienia (1 dla emaila, 2 dla SMS-a) oraz wiadomość do wysłania.
Użyj funkcji konstruktora, aby utworzyć odpowiedni powiadamiacz, a następnie wywołaj notify przez interfejs, aby wyświetlić wiadomość.
Przykładowy wynik, gdy typ to 1, a wiadomość to Meeting at 3pm:
[EMAIL] Meeting at 3pmPrzykładowy wynik, gdy typ to 2, a wiadomość to Your code shipped:
[SMS] Your code shippedZaletą tego wzorca jest to, że main.c nie musi wiedzieć, jak każdy powiadamiacz działa wewnętrznie. Po prostu wywołuje funkcję interfejsu notify, a wykonuje się właściwa implementacja. Pamiętaj, aby użyć strażników dołączania w pliku nagłówkowym.
Spróbuj swoich sił
#include <stdio.h>
#include "notifier.h"
int main() {
int type;
char message[256];
// Odczytaj typ powiadomienia (1 dla e-maila, 2 dla SMS-a)
scanf("%d", &type);
getchar(); // pobierz znak nowej linii
// Odczytaj wiadomość
fgets(message, sizeof(message), stdin);
// Usuń końcowy znak nowej linii, jeśli występuje
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Utwórz odpowiedni obiekt powiadamiający na podstawie type
// Użyj create_email_notifier() dla type 1
// Użyj create_sms_notifier() dla type 2
// TODO: Wywołaj funkcję notify przez interfejs
// notifier.notify(message);
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania modularnego
Pliki nagłówkoweZabezpieczenia przed wielokrotnym dołączeniemPliki źródłoweFunkcje statycznePodsumowanie: kalkulator modularny4Enkapsulacja
Koncepcja niejawnych wskaźnikówDefiniowanie niejawnych strukturGettery i setteryWalidacja w setterachPowtórka: tajemnicze pudełko2Obiekty i metody
Struktury jako obiektyWskaźnik „self”Poprawność constWskaźnik czy wartośćMetody pomocniczePodsumowanie: menedżer punktów5Projekt: Proste konto bankowe
Konfiguracja projektuImplementacja konta8Polimorfizm
Wskaźniki do funkcji w strukturachSymulowanie metodKoncepcja interfejsuImplementowanie interfejsówIteracja polimorficznaPodsumowanie: Greeter11Wzorce projektowe w C
Wzorzec SingletonWzorzec fabrykiWzorzec iteratoraPodsumowanie: fabryka loggera3Cykl życia obiektu
Wzorzec konstruktoraWzorzec destruktoraInicjalizacja na stosieKopia głębokaPodsumowanie: klasa opakowująca ciąg znaków6Dziedziczenie przez kompozycję
Osadzanie strukturZasada pierwszego elementuDostęp do elementów klasy nadrzędnejKonwersja w górę hierarchiiPodsumowanie: hierarchia kształtów9Projekt: Rysownik kształtów
Przegląd projektuImplementacja kołaImplementacja prostokątaZastosowanie polimorfizmuKontener kształtówPoćwicz samodzielnie: Kompilator C online