Menu
Coddy logo textTech

Validação em Setters

Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 20 de 61.

Na lição anterior, criamos setters básicos que simplesmente atribuem valores. Mas isso ignora um benefício fundamental do encapsulamento: a capacidade de proteger seus dados adicionando lógica de validação.

Considere um Counter que nunca deve se tornar negativo. Sem encapsulamento, qualquer pessoa poderia escrever c->value = -50 e corromper o estado. Com um setter, você controla quais valores são permitidos:

// counter.c
void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // Rejeita entrada inválida silenciosamente
    }
    c->value = new_value;
}

Agora, os valores negativos são simplesmente ignorados.

Você também pode optar por retornar um código de status para indicar sucesso ou falha:

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

Este padrão permite que o chamador saiba se a operação foi bem-sucedida. A percepção principal é que os setters não servem apenas para atribuição — eles são guardiões que impõem regras sobre como o estado do seu objeto pode ser. Este é o real poder do encapsulamento: seu objeto mantém sua própria integridade, independentemente do que o código externo tente fazer.

challenge icon

Desafio

Fácil

Vamos construir um módulo Temperature que protege seus dados usando validação no setter. Você criará um rastreador de temperatura que aceita apenas valores dentro de um intervalo válido — demonstrando como os setters atuam como guardiões do estado do seu objeto.

Você criará três arquivos:

  • temperature.h: Sua interface pública usando o padrão de ponteiro opaco (opaque pointer). Declare um tipo Temperature apenas com uma declaração antecipada (forward declaration). Declare estas funções:
    • create_temperature — recebe um inteiro para os graus iniciais e retorna um ponteiro Temperature
    • free_temperature — libera a memória alocada
    • temp_get_degrees — getter que retorna o valor atual da temperatura
    • temp_set_degrees — setter que retorna um int (1 para sucesso, 0 para falha)
    Use include guards com o símbolo TEMPERATURE_H.
  • temperature.c: Defina a struct Temperature real com um membro int degrees oculto. Implemente todas as funções. A chave é o seu setter — ele deve aceitar apenas valores entre -50 e 150 (inclusive). Se o valor estiver fora deste intervalo, retorne 0 e deixe a temperatura inalterada. Se for válido, atualize o valor e retorne 1.
  • main.c: Demonstre a validação tentando definir temperaturas válidas e inválidas, mostrando que valores inválidos são rejeitados enquanto os válidos são aceitos.

Você receberá três entradas: a temperatura inicial, uma temperatura válida para definir (dentro do intervalo) e uma temperatura inválida para tentar (fora do intervalo).

No seu arquivo principal, crie uma Temperature com o valor inicial e imprima-o. Em seguida, tente definir a temperatura inválida — como ela deve ser rejeitada, imprima o resultado e mostre que o valor permanece inalterado. Por fim, defina a temperatura válida, imprima o resultado de sucesso e mostre o valor atualizado. Libere a memória e confirme a limpeza.

Imprima a saída neste formato:

Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
Freed

Por exemplo, com as entradas 25, 100 e 200, a saída seria:

Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
Freed

Isso demonstra o real poder do encapsulamento — seu setter impõe as regras sobre o que constitui uma temperatura válida, protegendo a integridade do objeto, independentemente de quais valores o código externo tente atribuir.

Folha de consulta

Setters podem incluir lógica de validação para proteger a integridade dos dados. Em vez de simplesmente atribuir valores, eles atuam como gatekeepers que impõem regras sobre estados de objeto válidos.

Exemplo básico de validação - rejeitando entrada inválida silenciosamente:

void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // Rejeita entrada inválida
    }
    c->value = new_value;
}

Retornando um código de status para indicar sucesso ou falha:

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

Este padrão permite que os chamadores verifiquem se a operação foi bem-sucedida, permitindo que os objetos mantenham sua própria integridade, independentemente de tentativas de código externo.

Experimente você mesmo

#include <stdio.h>
#include "temperature.h"

int main() {
    // Ler as entradas
    int initial, valid_temp, invalid_temp;
    scanf("%d", &initial);
    scanf("%d", &valid_temp);
    scanf("%d", &invalid_temp);
    
    // TODO: Criar uma Temperature com o valor inicial
    
    // TODO: Imprimir a temperatura inicial
    // Formato: "Initial: {degrees}"
    
    // TODO: Tentar definir a temperatura inválida
    // Imprimir o resultado e mostrar que o valor permanece inalterado
    // Formato: "Set {invalid}: {0 or 1}"
    // Formato: "After invalid: {degrees}"
    
    // TODO: Definir a temperatura válida
    // Imprimir o resultado de sucesso e mostrar o valor atualizado
    // Formato: "Set {valid}: {0 or 1}"
    // Formato: "After valid: {degrees}"
    
    // TODO: Liberar a memória e imprimir "Freed"
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming