Koncepcja interfejsu
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 40 z 61.
Do tej pory nasze struktury łączyły dane i zachowanie: na przykład nazwę obok wskaźnika do funkcji. Ale co, jeśli struktura zawierałaby wyłącznie wskaźniki do funkcji? Powstaje wtedy coś potężnego: interfejs.
Interfejs określa, jakie operacje są dostępne, nie precyzując, jak działają. To kontrakt, który mówi: „każdy obiekt implementujący ten interfejs musi udostępniać te funkcje”.
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;Struktura ILogger zawiera wyłącznie wskaźnik do funkcji. Nie ma własnych danych. Opisuje wyłącznie zachowanie. Prefiks „I” to powszechna konwencja wskazująca, że jest to interfejs.
Żaden kod, który przyjmuje ILogger, nie przejmuje się tym, dokąd trafia komunikat dziennika. Może go wypisywać w konsoli, zapisywać w pliku lub wysyłać dane przez sieć. Wywołujący wie tylko, że może wywołać log:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // Nie wiemy, JAK zapisuje logi
}To rozdzielenie jest istotą polimorfizmu. Funkcja process działa z dowolną implementacją rejestratora. Możesz zmieniać zachowanie bez zmieniania kodu funkcji. W następnej lekcji utworzysz konkretne implementacje spełniające ten kontrakt interfejsu.
Wyzwanie
ŁatwyZbudujmy interfejs ICalculator: strukturę, która zawiera wyłącznie wskaźniki do funkcji, określającą, jakie operacje musi obsługiwać każdy kalkulator, bez precyzowania, jak mają działać.
Utworzysz dwa pliki, aby uporządkować swój kod:
calculator.h: Zdefiniuj tutaj swój interfejs. Utwórz typ wskaźnika do funkcji o nazwieBinaryOp, która przyjmuje dwa parametryinti zwracaint. Następnie zdefiniuj strukturęICalculator, która zawiera tylko dwa wskaźniki do funkcji:operate(typuBinaryOp) orazdescribe(wskaźnik do funkcji, która nie przyjmuje parametrów i niczego nie zwraca). Ta struktura jest wyłącznie interfejsem. Nie przechowuje żadnych danych, tylko zachowanie.main.c: Dołącz plik nagłówkowy i zaimplementuj konkretne funkcje, które spełniają wymagania interfejsu:add: zwraca sumę dwóch liczb całkowitychmultiply: zwraca iloczyn dwóch liczb całkowitychdescribe_add: wyświetlaAdditiondescribe_multiply: wyświetlaMultiplication
ICalculator, przypisz jej odpowiednie funkcje na podstawie danych wejściowych i użyj jej do wykonania obliczenia.
Program otrzyma trzy dane wejściowe: dwie liczby całkowite i kod operacji (1 oznacza dodawanie, 2 mnożenie).
Na podstawie kodu operacji przypisz do ICalculator odpowiednią parę funkcji. Następnie wywołaj describe za pośrednictwem interfejsu, aby wyświetlić nazwę operacji, a potem wywołaj operate, aby obliczyć i wyświetlić wynik.
Przykładowy wynik, gdy dane wejściowe to 5, 3 i 1:
Addition
8Przykładowy wynik, gdy dane wejściowe to 4, 7 i 2:
Multiplication
28Najważniejsze jest to, że struktura ICalculator definiuje kontrakt: każdy kod, który z niej korzysta, wie, że może wywołać describe i operate, nie wiedząc, które konkretne funkcje zostały przypisane. Pamiętaj o użyciu strażników dołączania w pliku nagłówkowym.
Spróbuj swoich sił
#include <stdio.h>
#include "calculator.h"
// TODO: Zaimplementuj funkcję add
// Zwraca sumę dwóch liczb całkowitych
// TODO: Zaimplementuj funkcję multiply
// Zwraca iloczyn dwóch liczb całkowitych
// TODO: Zaimplementuj funkcję describe_add
// Wypisuje "Addition"
// TODO: Zaimplementuj funkcję describe_multiply
// Wypisuje "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: Utwórz instancję ICalculator
// TODO: Na podstawie op_code (1 dla add, 2 dla multiply)
// przypisz odpowiednie funkcje do kalkulatora
// TODO: Wywołaj describe przez interfejs
// TODO: Wywołaj operate przez interfejs i wypisz wynik
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