Validación en setters
Parte de la sección Programación Orientada a Objetos 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 pasa por alto 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, controlas qué valores están permitidos:
// counter.c
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Rechaza la entrada inválida silenciosamente
}
c->value = new_value;
}Ahora los valores negativos simplemente se ignoran.
También podrías optar por devolver un código de estado para indicar éxito o 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 que quien realiza la llamada sepa si la operación tuvo éxito. La idea clave es que los setters no se limitan a la asignación: son guardianes que hacen cumplir las reglas sobre cuál puede ser el estado de tu objeto. Este es el verdadero poder de la encapsulación: tu objeto mantiene su propia integridad, independientemente de lo que el código externo intente hacer.
Desafío
FácilConstruyamos un módulo Temperature que proteja sus datos mediante la validación en el setter. Crearás un rastreador de temperatura que solo acepta 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 mediante el patrón de puntero opaco. Declara un tipoTemperatureúnicamente con una declaración anticipada. Declara estas funciones:create_temperature: recibe un entero para los grados iniciales y devuelve un puntero a Temperaturefree_temperature: libera la memoria asignadatemp_get_degrees: getter que devuelve el valor actual de la temperaturatemp_set_degrees: setter que devuelve unint(1 para indicar éxito, 0 para indicar fallo)
TEMPERATURE_H.temperature.c: Define lastruct 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 temperaturas válidas y no válidas, mostrando que los valores no válidos se rechazan mientras que los válidos se aceptan.
Recibirás tres entradas: la temperatura inicial, una temperatura válida que establecer (dentro del rango) y una temperatura no válida que intentar establecer (fuera del rango).
En tu archivo principal, crea un Temperature con el valor inicial e imprímelo. Después, intenta establecer la temperatura no válida, ya que debería rechazarse; 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 con 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 de la encapsulación. Tu setter aplica las reglas sobre lo que constituye una temperatura válida, protegiendo la integridad del objeto independientemente de los valores que el código externo intente asignar.
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: Crea un Temperature con el valor inicial
// TODO: Imprime la temperatura inicial
// Formato: "Initial: {degrees}"
// TODO: Intenta establecer la temperatura inválida
// Imprime el resultado y muestra que el valor permanece sin cambios
// Formato: "Set {invalid}: {0 or 1}"
// Format: "After invalid: {degrees}"
// TODO: Establece la temperatura válida
// Imprime el resultado exitoso y muestra el valor actualizado
// Formato: "Set {valid}: {0 or 1}"
// Formato: "After valid: {degrees}"
// TODO: Libera la memoria e imprime "Freed"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
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írculoImplementación del rectánguloUso polimórficoContenedor de figurasPractica por tu cuenta: Compilador de C online