Menu
Coddy logo textTech

Validation dans les setters

Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 20 sur 61.

Dans la leçon précédente, nous avons créé des setters basiques qui assignent simplement des valeurs. Mais cela fait l'impasse sur un avantage clé de l'encapsulation : la possibilité de protéger vos données en ajoutant une logique de validation.

Considérez 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 setter, vous contrôlez quelles valeurs sont autorisées :

// counter.c
void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // Rejeter silencieusement les entrées non valides
    }
    c->value = new_value;
}

Maintenant, les valeurs négatives sont simplement ignorées.

Vous pourriez également choisir de renvoyer un code d'état pour indiquer le succès 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 clé est que les setters ne servent pas uniquement à l'affectation — ce sont des gardiens qui imposent des règles sur ce que peut être l'état de votre objet. C'est là le véritable pouvoir de l'encapsulation : votre objet maintient sa propre intégrité, peu importe ce que le code externe tente de faire.

challenge icon

Défi

Facile

Construisons un module Temperature qui protège ses données en utilisant la validation dans le setter. Vous allez créer un suivi de température qui n'accepte que des valeurs comprises dans une plage valide, démontrant ainsi comment les setters agissent comme des gardiens pour l'état de votre objet.

Vous allez créer trois fichiers :

  • temperature.h : Votre interface publique utilisant le modèle du pointeur opaque. Déclarez un type Temperature avec une déclaration anticipée (forward declaration) uniquement. Déclarez ces fonctions :
    • create_temperature — prend un entier pour les degrés initiaux et retourne un pointeur Temperature
    • free_temperature — libère la mémoire allouée
    • temp_get_degrees — getter qui retourne la valeur actuelle de la température
    • temp_set_degrees — setter qui retourne un int (1 pour le succès, 0 pour l'échec)
    Utilisez des gardes d'inclusion avec le symbole TEMPERATURE_H.
  • temperature.c : Définissez la structure réelle struct Temperature avec un membre int degrees caché. Implémentez toutes les fonctions. La clé est votre setter — il ne doit accepter que des valeurs comprises entre -50 et 150 (inclus). Si la valeur est en dehors de cette plage, retournez 0 et laissez la température inchangée. Si elle est valide, mettez à jour la valeur et retournez 1.
  • main.c : Démontrez la validation en essayant de définir des températures valides et invalides, montrant que les valeurs invalides sont rejetées tandis que les valeurs valides sont acceptées.

Vous recevrez trois entrées : la température initiale, une température valide à définir (dans la plage), et une température invalide à tenter (hors de la plage).

Dans votre fichier principal, créez une Temperature avec la valeur initiale et affichez-la. Essayez ensuite de définir la température invalide — comme elle doit être rejetée, affichez le résultat et montrez que la valeur reste inchangée. Enfin, définissez la température valide, affichez le résultat de succès et montrez la valeur mise à jour. Libérez la mémoire et confirmez le nettoyage.

Affichez 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 démontre le véritable pouvoir de l'encapsulation — votre setter applique les règles sur ce qui constitue une température valide, protégeant l'intégrité de l'objet quelles que soient les valeurs que le code externe tente d'assigner.

Aide-mémoire

Les setters peuvent inclure une logique de validation pour protéger l'intégrité des données. Au lieu de simplement assigner des valeurs, ils agissent comme des gardiens qui imposent des règles sur les états valides des objets.

Exemple de validation de base - rejet silencieux d'une entrée invalide :

void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // Reject invalid input
    }
    c->value = new_value;
}

Retourner un code d'état pour indiquer le succès ou l'échec :

int counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return 0;  // Failure
    }
    c->value = new_value;
    return 1;  // Success
}

Ce modèle permet aux appelants de vérifier si l'opération a réussi, permettant ainsi aux objets de maintenir leur propre intégrité indépendamment des tentatives du code externe.

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 une Temperature avec la valeur initiale
    
    // TODO: Afficher la température initiale
    // Format: "Initial: {degrees}"
    
    // TODO: Tenter 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 de succès 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 Object Oriented Programming