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 kluczową zaletę enkapsulacji: możliwość ochrony danych przez dodanie logiki walidacji.
Rozważ Counter, który nigdy nie powinien przyjmować wartości ujemnych. Bez hermetyzacji każdy mógłby napisać c->value = -50 i naruszyć 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 także 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; // Powodzenie
}Ten wzorzec pozwala wywołującemu sprawdzić, czy operacja się powiodła. 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 Twojego obiektu. Na tym polega prawdziwa siła enkapsulacji: obiekt zachowuje własną integralność, niezależnie od tego, co próbuje zrobić zewnętrzny kod.
Wyzwanie
ŁatwyZbudujmy moduł Temperature, który chroni swoje dane, stosując walidację w setterze. Utworzysz moduł śledzący temperaturę, który akceptuje tylko wartości z prawidłowego zakresu — pokażemy, jak settery pełnią rolę strażników stanu obiektu.
Utworzysz trzy pliki:
temperature.h: publiczny interfejs wykorzystujący wzorzec nieprzezroczystego wskaźnika. Zadeklaruj typTemperaturewyłącznie przez deklarację wstępną. Zadeklaruj następujące funkcje:create_temperature: przyjmuje liczbę całkowitą oznaczającą początkową temperaturę 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ć wyłącznie wartości z zakresu od -50 do 150 (włącznie). Jeśli wartość wykracza poza ten zakres, zwróć 0 i pozostaw temperaturę bez zmian. Jeśli jest prawidłowa, zaktualizuj wartość i zwróć 1.main.c: Zademonstruj walidację, próbując ustawić zarówno prawidłowe, jak i nieprawidłowe temperatury, i pokaż, że nieprawidłowe wartości są odrzucane, a prawidłowe akceptowane.
Otrzymasz trzy wartości wejściowe: temperaturę początkową, prawidłową temperaturę do ustawienia (z zakresu) oraz nieprawidłową temperaturę, której ustawienia należy spróbować (spoza zakresu).
W pliku głównym utwórz obiekt Temperature z wartością początkową i wyświetl ją. Następnie spróbuj ustawić nieprawidłową temperaturę; ponieważ powinna zostać odrzucona, wyświetl wynik i pokaż, że wartość pozostała bez zmian. Na koniec ustaw prawidłową temperaturę, wyświetl informację o powodzeniu i pokaż zaktualizowaną wartość. Zwolnij pamięć i potwierdź czyszczenie.
Wyświetl 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 byłby następujący:
Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
FreedTo pokazuje prawdziwą siłę hermetyzacji. Twój 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ć do niego kod zewnętrzny.
Spróbuj swoich sił
#include <stdio.h>
#include "temperature.h"
int main() {
// Odczytaj 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