Walidacja w setterach
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 20 z 61.
W poprzedniej lekcji utworzyliśmy podstawowe funkcje ustawiające, które po prostu przypisują wartości. Pomija to jednak ważną zaletę enkapsulacji: możliwość ochrony danych przez dodanie logiki walidacji.
Rozważmy Counter, który nigdy nie powinien przyjmować wartości ujemnych. Bez enkapsulacji każdy mógłby napisać c->value = -50 i uszkodzić stan. Dzięki metodzie ustawiającej kontrolujesz, jakie wartości są dozwolone:
// counter.c
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Odrzuć nieprawidłowe dane wejściowe bez komunikatu
}
c->value = new_value;
}Teraz wartości ujemne są po prostu ignorowane.
Możesz też zdecydować się zwrócić kod statusu, aby wskazać powodzenie lub niepowodzenie:
int counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return 0; // Niepowodzenie
}
c->value = new_value;
return 1; // Sukces
}Ten wzorzec pozwala wywołującemu dowiedzieć się, czy operacja zakończyła się powodzeniem. Kluczowy wniosek jest taki, że settery nie służą wyłącznie do przypisywania wartości: są strażnikami, którzy egzekwują zasady dotyczące tego, jaki może być stan obiektu. Na tym polega prawdziwa siła enkapsulacji: obiekt zachowuje swoją integralność, niezależnie od tego, co próbuje zrobić zewnętrzny kod.
Wyzwanie
ŁatwyZbudujmy moduł Temperature, który chroni swoje dane, sprawdzając je w setterze. Utworzysz rejestrator temperatury, który akceptuje tylko wartości z prawidłowego zakresu — pokażemy, jak settery pilnują stanu obiektu.
Utworzysz trzy pliki:
temperature.h: publiczny interfejs wykorzystujący wzorzec nieprzezroczystego wskaźnika. Zadeklaruj typTemperaturewyłącznie za pomocą deklaracji wstępnej. Zadeklaruj następujące funkcje:create_temperature: przyjmuje liczbę całkowitą oznaczającą temperaturę początkową i zwraca wskaźnik do Temperaturefree_temperature: zwalnia przydzieloną pamięćtemp_get_degrees: getter zwracający bieżącą wartość temperaturytemp_set_degrees: setter zwracającyint(1 w przypadku powodzenia, 0 w przypadku niepowodzenia)
TEMPERATURE_H.temperature.c: Zdefiniuj właściwą strukturęstruct Temperaturez ukrytym polemint degrees. Zaimplementuj wszystkie funkcje. Kluczowy jest setter. Powinien akceptować tylko wartości z zakresu od -50 do 150 (włącznie). Jeśli wartość jest poza tym zakresem, zwróć 0 i pozostaw temperaturę bez zmian. Jeśli wartość jest prawidłowa, zaktualizuj ją i zwróć 1.main.c: Pokaż działanie walidacji, próbując ustawić zarówno prawidłowe, jak i nieprawidłowe temperatury, aby wykazać, że nieprawidłowe wartości są odrzucane, a prawidłowe akceptowane.
Otrzymasz trzy dane wejściowe: temperaturę początkową, prawidłową temperaturę do ustawienia (z zakresu) oraz nieprawidłową temperaturę do sprawdzenia (spoza zakresu).
W pliku głównym utwórz Temperature z początkową wartością i ją wypisz. Następnie spróbuj ustawić nieprawidłową temperaturę, a ponieważ powinna zostać odrzucona, wypisz wynik i pokaż, że wartość pozostała bez zmian. Na koniec ustaw prawidłową temperaturę, wypisz informację o powodzeniu i pokaż zaktualizowaną wartość. Zwolnij pamięć i potwierdź sprzątanie.
Wypisz wynik w tym formacie:
Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
FreedNa przykład dla danych wejściowych 25, 100 i 200 wynik wyglądałby tak:
Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
FreedTo pokazuje prawdziwą moc hermetyzacji. Setter egzekwuje zasady określające, jaka temperatura jest prawidłowa, chroniąc integralność obiektu niezależnie od tego, jakie wartości próbuje przypisać kod zewnętrzny.
Spróbuj swoich sił
#include <stdio.h>
#include "temperature.h"
int main() {
// Wczytaj dane wejściowe
int initial, valid_temp, invalid_temp;
scanf("%d", &initial);
scanf("%d", &valid_temp);
scanf("%d", &invalid_temp);
// TODO: Utwórz obiekt Temperature z wartością początkową
// TODO: Wypisz temperaturę początkową
// Format: "Initial: {degrees}"
// TODO: Spróbuj ustawić nieprawidłową temperaturę
// Wypisz wynik i pokaż, że wartość pozostała bez zmian
// Format: "Set {invalid}: {0 or 1}"
// Format: "After invalid: {degrees}"
// TODO: Ustaw prawidłową temperaturę
// Wypisz wynik operacji i pokaż zaktualizowaną wartość
// Format: "Set {valid}: {0 or 1}"
// Format: "After valid: {degrees}"
// TODO: Zwolnij pamięć i wypisz "Freed"
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