La convalida nei setter
Fa parte della sezione Programmazione orientata agli oggetti del percorso C di Coddy. Lezione 20 di 61.
Nella lezione precedente, abbiamo creato dei setter di base che si limitano ad assegnare valori. Ma così si perde un vantaggio fondamentale dell'incapsulamento: la possibilità di proteggere i tuoi dati aggiungendo una logica di convalida.
Considera un Counter che non dovrebbe mai diventare negativo. Senza incapsulamento, chiunque potrebbe scrivere c->value = -50 e corrompere lo stato. Con un setter, controlli quali valori sono consentiti:
// counter.c
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Rifiuta l'input non valido senza avvisare
}
c->value = new_value;
}Ora i valori negativi vengono semplicemente ignorati.
Potresti anche scegliere di restituire un codice di stato per indicare il successo o il fallimento:
int counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return 0; // Errore
}
c->value = new_value;
return 1; // Operazione riuscita
}Questo schema consente al chiamante di sapere se l'operazione è riuscita. L'intuizione chiave è che i setter non servono solo ad assegnare valori: sono custodi che applicano regole su quali possono essere gli stati del tuo oggetto. Questo è il vero potere dell'incapsulamento: il tuo oggetto mantiene la propria integrità, indipendentemente da ciò che il codice esterno cerca di fare.
Sfida
FacileCostruiamo un modulo Temperature che protegge i propri dati tramite la convalida nel setter. Creerai un tracker della temperatura che accetta solo valori entro un intervallo valido: vedrai come i setter fungono da guardiani dello stato del tuo oggetto.
Creerai tre file:
temperature.h: la tua interfaccia pubblica che usa il pattern del puntatore opaco. Dichiara un tipoTemperaturecon la sola dichiarazione anticipata. Dichiara queste funzioni:create_temperature: accetta un intero per i gradi iniziali e restituisce un puntatore a Temperaturefree_temperature: rilascia la memoria allocatatemp_get_degrees: getter che restituisce il valore attuale della temperaturatemp_set_degrees: setter che restituisce unint(1 in caso di successo, 0 in caso di errore)
TEMPERATURE_H.temperature.c: definisci la verastruct Temperaturecon un membroint degreesnascosto. Implementa tutte le funzioni. Il punto fondamentale è il setter. Dovrebbe accettare solo valori compresi tra -50 e 150 (inclusi). Se il valore è fuori da questo intervallo, restituisci 0 e lascia invariata la temperatura. Se è valido, aggiorna il valore e restituisci 1.main.c: mostra la convalida provando a impostare temperature valide e non valide, mostrando che i valori non validi vengono rifiutati, mentre quelli validi vengono accettati.
Riceverai tre input: la temperatura iniziale, una temperatura valida da impostare (entro l'intervallo) e una temperatura non valida da provare (fuori dall'intervallo).
Nel tuo file principale, crea una Temperature con il valore iniziale e stampalo. Poi prova a impostare la temperatura non valida; poiché dovrebbe essere rifiutata, stampa il risultato e mostra che il valore è rimasto invariato. Infine, imposta la temperatura valida, stampa il risultato dell'operazione e mostra il valore aggiornato. Libera la memoria e conferma la pulizia.
Stampa l'output in questo formato:
Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
FreedPer esempio, con gli input 25, 100 e 200, l'output sarebbe:
Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
FreedQuesto dimostra la vera efficacia dell'incapsulamento. Il tuo setter impone le regole che definiscono una temperatura valida, proteggendo l'integrità dell'oggetto indipendentemente dai valori che il codice esterno cerca di assegnare.
Provalo tu
#include <stdio.h>
#include "temperature.h"
int main() {
// Leggi gli input
int initial, valid_temp, invalid_temp;
scanf("%d", &initial);
scanf("%d", &valid_temp);
scanf("%d", &invalid_temp);
// TODO: Crea un oggetto Temperature con il valore iniziale
// TODO: Stampa la temperatura iniziale
// Formato: "Initial: {degrees}"
// TODO: Prova a impostare la temperatura non valida
// Stampa il risultato e mostra che il valore rimane invariato
// Formato: "Set {invalid}: {0 or 1}"
// Formato: "After invalid: {degrees}"
// TODO: Imposta la temperatura valida
// Stampa il risultato dell'operazione e mostra il valore aggiornato
// Formato: "Set {valid}: {0 or 1}"
// Formato: "After valid: {degrees}"
// TODO: Libera la memoria e stampa "Freed"
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Basi di programmazione modulare
File di intestazioneGuardie di inclusioneFile sorgenteFunzioni staticheRipasso: calcolatrice modulare4Incapsulamento
Il concetto di puntatori opachiDefinire struct opacheGetter e setterLa convalida nei setterRiepilogo: scatola segreta2Oggetti e metodi
Le struct come oggettiIl puntatore 'Self'Correttezza constPuntatore o valoreMetodi di supportoRiepilogo: gestore di punti5Progetto: Conto bancario semplice
Configurazione del progettoImplementazione del conto8Polimorfismo
Puntatori a funzione nelle structSimulare i metodiIl concetto di interfacciaImplementare le interfacceIterazione polimorficaRiepilogo: Greeter11Pattern di progettazione in C
Pattern SingletonPattern FactoryPattern IteratorRiepilogo: Factory di Logger3Ciclo di vita degli oggetti
Pattern del costruttorePattern del distruttoreInizializzazione sullo stackCopia profondaRiepilogo: wrapper di stringhe6Ereditarietà tramite composizione
Incorporamento delle structLa regola del primo membroAccesso ai membri della classe baseUpcastingRiepilogo: gerarchia di forme9Progetto: Disegnatore di forme
Panoramica del progettoImplementazione del cerchioImplementazione del rettangoloUtilizzo polimorficoContenitore di formeEsercitati da solo: Compilatore C online