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 una ventaja 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 se permiten:
// counter.c
void counter_set_value(Counter *c, int new_value) {
if (new_value < 0) {
return; // Rechaza silenciosamente la entrada no válida
}
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 si la operación se realizó correctamente o no:
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 se completó correctamente. La idea clave es que los métodos setter no se limitan a asignar valores: son guardianes que hacen cumplir las reglas sobre el estado que puede tener tu objeto. Este es el verdadero poder de la encapsulación: tu objeto mantiene su propia integridad, independientemente de lo que intente hacer el código externo.
Desafío
FácilVamos a crear un módulo Temperature que proteja sus datos mediante validación en el setter. Crearás un rastreador de temperatura que solo acepte valores dentro de un rango válido: así demostrarás cómo los setters actúan como guardianes del estado de tu objeto.
Crearás tres archivos:
temperature.h: Tu interfaz pública, que utiliza 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 si tiene éxito, 0 si falla)
TEMPERATURE_H.temperature.c: Define lastruct Temperaturereal con un miembroint degreesoculto. Implementa todas las funciones. La clave está en el 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 e inválidas, y mostrando que los valores inválidos se rechazan mientras que los válidos se aceptan.
Recibirás tres entradas: la temperatura inicial, una temperatura válida para establecer (dentro del rango) y una temperatura inválida para intentar establecer (fuera del rango).
En tu archivo main, crea una Temperature con el valor inicial e imprímela. Luego intenta establecer la temperatura inválida; como debería rechazarse, imprime el resultado y muestra que el valor permanece sin cambios. Por último, establece la temperatura válida, imprime el resultado satisfactorio y muestra el valor actualizado. Libera la memoria y confirma que se ha limpiado.
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 que determinan qué constituye una temperatura válida y protege 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() {
// Lee las 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 no válida
// Imprime el resultado y muestra que el valor permanece sin cambios
// Formato: "Set {invalid}: {0 or 1}"
// Formato: "After invalid: {degrees}"
// TODO: Establece la temperatura válida
// Imprime el resultado de la operación 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