Menu
CoddyTech

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.

challenge icon

Défi

Facile

Construisons 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 type Temperature uniquement 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 Temperature
    • free_temperature : libère la mémoire allouée
    • temp_get_degrees : getter qui renvoie la valeur actuelle de la température
    • temp_set_degrees : setter qui renvoie un int (1 en cas de réussite, 0 en cas d'échec)
    Utilise des gardes d'inclusion avec le symbole TEMPERATURE_H.
  • temperature.c : définis le véritable struct Temperature avec un membre int degrees masqué. 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}
Freed

Par 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
Freed

Cela 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;
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur C en ligne