Validación en setters
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 20 de 61.
En la lección anterior, creamos setters básicos que simplemente asignan valores. Pero esto omite un beneficio clave de la encapsulación: la capacidad de proteger tus datos añadiendo lógica de validación.
Considera un Counter que nunca debería ser negativo. Sin encapsulación, cualquiera podría escribir c->value = -50 y corromper el estado. Con un setter, tú controlas qué valores están permitidos:
// counter.c
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Rechazar entrada no válida silenciosamente
}
c->value = new_value;
}Ahora los valores negativos simplemente se ignoran.
También puede optar por devolver un código de estado para indicar el éxito o el fracaso:
int counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return 0; // Fallo
}
c->value = new_value;
return 1; // Éxito
}Este patrón permite al llamador saber si la operación tuvo éxito. La idea clave es que los setters no se limitan solo a la asignación; son guardianes que imponen reglas sobre cuál puede ser el estado de su objeto. Este es el verdadero poder de la encapsulación: su objeto mantiene su propia integridad, independientemente de lo que el código externo intente hacer.
Desafío
FácilVamos a construir un módulo Temperature que proteja sus datos utilizando validación en el setter. Crearás un rastreador de temperatura que solo acepte valores dentro de un rango válido, demostrando cómo los setters actúan como guardianes del estado de tu objeto.
Crearás tres archivos:
temperature.h: Tu interfaz pública utilizando el patrón de puntero opaco (opaque pointer). Declara un tipoTemperaturesolo con una declaración anticipada (forward declaration). Declara estas funciones:create_temperature— toma un entero para los grados iniciales y devuelve un puntero Temperaturefree_temperature— libera la memoria asignadatemp_get_degrees— getter que devuelve el valor de temperatura actualtemp_set_degrees— setter que devuelve unint(1 para éxito, 0 para fallo)
TEMPERATURE_H.temperature.c: Define elstruct Temperaturereal con un miembroint degreesoculto. Implementa todas las funciones. La clave es tu setter: solo debe aceptar valores entre -50 y 150 (inclusive). Si el valor está fuera de este rango, devuelve 0 y deja la temperatura sin cambios. Si es válido, actualiza el valor y devuelve 1.main.c: Demuestra la validación intentando establecer tanto temperaturas válidas como inválidas, mostrando que los valores inválidos son rechazados mientras que los válidos son aceptados.
Recibirás tres entradas: la temperatura inicial, una temperatura válida para establecer (dentro del rango) y una temperatura inválida para intentar (fuera del rango).
In tu archivo principal, crea una Temperature con el valor inicial e imprímela. Luego intenta establecer la temperatura inválida; dado que debería ser rechazada, imprime el resultado y muestra que el valor permanece sin cambios. Finalmente, establece la temperatura válida, imprime el resultado de éxito y muestra el valor actualizado. Libera la memoria y confirma la limpieza.
Imprime la salida en este formato:
Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
FreedPor ejemplo, con las entradas 25, 100 y 200, la salida sería:
Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
FreedEsto demuestra el verdadero poder del encapsulamiento: tu setter impone las reglas sobre lo que constituye una temperatura válida, protegiendo la integridad del objeto independientemente de qué valores intente asignar el código externo.
Hoja de referencia
Los setters pueden incluir lógica de validación para proteger la integridad de los datos. En lugar de simplemente asignar valores, actúan como guardianes que imponen reglas sobre los estados válidos de los objetos.
Ejemplo de validación básica: rechazar entradas no válidas de forma silenciosa:
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Rechazar entrada no válida
}
c->value = new_value;
}Devolver un código de estado para indicar éxito o fallo:
int counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return 0; // Fallo
}
c->value = new_value;
return 1; // Éxito
}Este patrón permite a quienes llaman a la función verificar si la operación tuvo éxito, permitiendo que los objetos mantengan su propia integridad independientemente de los intentos del código externo.
Pruébalo tú mismo
#include <stdio.h>
#include "temperature.h"
int main() {
// Leer entradas
int initial, valid_temp, invalid_temp;
scanf("%d", &initial);
scanf("%d", &valid_temp);
scanf("%d", &invalid_temp);
// TODO: Crear una Temperature con el valor inicial
// TODO: Imprimir la temperatura inicial
// Format: "Initial: {degrees}"
// TODO: Intentar establecer la temperatura no válida
// Imprimir el resultado y mostrar que el valor permanece sin cambios
// Format: "Set {invalid}: {0 or 1}"
// Format: "After invalid: {degrees}"
// TODO: Establecer la temperatura válida
// Imprimir el resultado de éxito y mostrar el valor actualizado
// Format: "Set {valid}: {0 or 1}"
// Format: "After valid: {degrees}"
// TODO: Liberar la memoria e imprimir "Freed"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo