Validation dans les setters
Fait partie de la section Programmation Orientée Objet du Journey C de Coddy. Leçon 20 sur 61.
Dans la leçon précédente, nous avons créé des accesseurs de base qui se contentent d’affecter des valeurs. Mais cela fait manquer un avantage essentiel de l’encapsulation : la possibilité de protéger vos données en ajoutant une logique de validation.
Considérons un Counter qui ne devrait jamais devenir négatif. Sans encapsulation, n'importe qui pourrait écrire c->value = -50 et corrompre l'état. Avec un accesseur de modification, tu contrôles les valeurs autorisées :
// counter.c
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Rejeter silencieusement une entrée invalide
}
c->value = new_value;
}Maintenant, les valeurs négatives sont simplement ignorées.
Tu peux aussi choisir de renvoyer un code d’état pour indiquer la réussite ou l’échec :
int counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return 0; // Échec
}
c->value = new_value;
return 1; // Succès
}Ce modèle permet à l’appelant de savoir si l’opération a réussi. L’idée essentielle est que les fonctions de définition ne servent pas uniquement à affecter une valeur : elles jouent le rôle de gardiens qui imposent des règles concernant l’état que peut avoir votre objet. C’est là tout le pouvoir de l’encapsulation : votre objet préserve sa propre intégrité, quelles que soient les tentatives du code externe.
Défi
FacileConstruisons un module Temperature qui protège ses données en validant les valeurs dans le setter. Tu vas créer un suivi de température qui n'accepte que les valeurs comprises dans une plage valide : cela montre comment les setters protègent l'état de ton objet.
Tu vas créer trois fichiers :
temperature.h: ton interface publique utilisant le modèle du pointeur opaque. Déclare un typeTemperatureuniquement avec une déclaration anticipée. Déclare les fonctions suivantes :create_temperature: prend un entier correspondant à la température initiale et renvoie un pointeur vers Temperaturefree_temperature: libère la mémoire allouéetemp_get_degrees: getter qui renvoie la valeur actuelle de la températuretemp_set_degrees: setter qui renvoie unint(1 en cas de réussite, 0 en cas d'échec)
TEMPERATURE_H.temperature.c: définis le véritablestruct Temperatureavec un membreint degreesmasqué. Implémente toutes les fonctions. L'essentiel est ton setter. Il ne doit accepter que les valeurs comprises entre -50 et 150 (inclus). Si la valeur est en dehors de cette plage, renvoie 0 et laisse la température inchangée. Si elle est valide, mets à jour la valeur et renvoie 1.main.c: montre la validation en tentant de définir des températures valides et invalides, et en montrant que les valeurs invalides sont rejetées tandis que les valeurs valides sont acceptées.
Tu recevras trois entrées : la température initiale, une température valide à définir (dans la plage) et une température invalide à essayer (en dehors de la plage).
Dans ton fichier main, crée une Temperature avec la valeur initiale et affiche-la. Tente ensuite de définir la température invalide ; comme elle doit être rejetée, affiche le résultat et montre que la valeur reste inchangée. Enfin, définis la température valide, affiche le résultat de réussite et montre la valeur mise à jour. Libère la mémoire et confirme le nettoyage.
Affiche la sortie dans ce format :
Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
FreedPar exemple, avec les entrées 25, 100 et 200, la sortie serait :
Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
FreedCela montre le véritable pouvoir de l'encapsulation. Ton setter applique les règles qui définissent une température valide, protégeant ainsi l'intégrité de l'objet, quelles que soient les valeurs que le code externe tente de lui attribuer.
Essayez vous-même
#include <stdio.h>
#include "temperature.h"
int main() {
// Lire les entrées
int initial, valid_temp, invalid_temp;
scanf("%d", &initial);
scanf("%d", &valid_temp);
scanf("%d", &invalid_temp);
// TODO: Créer un Temperature avec la valeur initiale
// TODO: Afficher la température initiale
// Format : "Initial: {degrees}"
// TODO: Essayer de définir la température invalide
// Afficher le résultat et montrer que la valeur reste inchangée
// Format : "Set {invalid}: {0 or 1}"
// Format : "After invalid: {degrees}"
// TODO: Définir la température valide
// Afficher le résultat indiquant la réussite et montrer la valeur mise à jour
// Format : "Set {valid}: {0 or 1}"
// Format : "After valid: {degrees}"
// TODO: Libérer la mémoire et afficher "Freed"
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Bases de la programmation modulaire
Fichiers d'en-têteGardes d'inclusionFichiers sourcesFonctions statiquesRécapitulatif : Calculatrice modulaire4Encapsulation
Concept des pointeurs opaquesDéfinir des structures opaquesGetters et settersValidation dans les settersRécapitulatif : La boîte secrète2Objets et méthodes
Structs comme objetsLe pointeur 'Self'Rigueur du mot-clé constPointeur vs ValeurMéthodes utilitairesRécapitulatif : Point Manager5Projet : Compte bancaire simple
Configuration du projetImplémentation du compte8Polymorphisme
Pointeurs de fonctions dans les structuresSimulation de méthodesLe concept d'interfaceImplémentation d'interfacesItération polymorphiqueRécapitulatif : Greeter11Patrons de conception en C
Patron SingletonPatron FabriquePatron ItérateurRécapitulatif : Logger Factory3Cycle de vie des objets
Pattern de constructeurPattern de destructeurInitialisation sur la pileCopie profondeRécapitulatif : String Wrapper6Héritage par composition
Imbrication de structLa règle du premier membreAccès aux membres parentsUpcastingRécapitulatif : Hiérarchie des formes9Projet : Dessinateur de formes
Aperçu du projetImplémentation du cercleImplémentation du rectangleUtilisation polymorpheConteneur de formesEntraînez-vous par vous-même : Compilateur C en ligne