Validação em setters
Parte da seção Programação Orientada a Objetos 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 deixa de lado um benefício importante do encapsulamento: a capacidade de proteger seus dados adicionando lógica de validação.
Considere um Counter que nunca deve ficar 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 entradas inválidas sem emitir mensagens
}
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
}Esse padrão permite que quem chama saiba se a operação foi bem-sucedida. A principal percepção é que os métodos setters não servem apenas para atribuição: eles são guardiões que impõem regras sobre quais estados seu objeto pode ter. Esse é o verdadeiro poder do encapsulamento: seu objeto mantém sua própria integridade, independentemente do que o código externo tente fazer.
Desafio
FácilVamos criar um módulo Temperature que protege seus dados usando validação no setter. Você vai criar um rastreador de temperatura que aceita apenas valores dentro de um intervalo válido, demonstrando como setters atuam como guardiões do estado do seu objeto.
Você vai criar três arquivos:
temperature.h: sua interface pública usando o padrão de ponteiro opaco. Declare um tipoTemperatureapenas com uma declaração antecipada. Declare estas funções:create_temperature: recebe um inteiro para os graus iniciais e retorna um ponteiro para Temperaturefree_temperature: libera a memória alocadatemp_get_degrees: getter que retorna o valor atual da temperaturatemp_set_degrees: setter que retorna umint(1 para sucesso, 0 para falha)
TEMPERATURE_H.temperature.c: defina astruct Temperaturereal com um membroint degreesoculto. Implemente todas as funções. O essencial é o setter. Ele deve aceitar apenas valores entre -50 e 150 (inclusive). Se o valor estiver fora desse intervalo, retorne 0 e mantenha 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 os 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 definir (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 do 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}
FreedPor 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
FreedIsso demonstra o verdadeiro poder do encapsulamento. Seu setter impõe as regras que definem o que constitui uma temperatura válida, protegendo a integridade do objeto independentemente dos valores que o código externo tente atribuir.
Experimente você mesmo
#include <stdio.h>
#include "temperature.h"
int main() {
// Leia as entradas
int initial, valid_temp, invalid_temp;
scanf("%d", &initial);
scanf("%d", &valid_temp);
scanf("%d", &invalid_temp);
// TODO: Crie uma Temperature com o valor inicial
// TODO: Imprima a temperatura inicial
// Formato: "Initial: {degrees}"
// TODO: Tente definir a temperatura inválida
// Imprima o resultado e mostre que o valor permanece inalterado
// Formato: "Set {invalid}: {0 or 1}"
// Formato: "After invalid: {degrees}"
// TODO: Defina a temperatura válida
// Imprima o resultado de sucesso e mostre o valor atualizado
// Formato: "Set {valid}: {0 or 1}"
// Formato: "After valid: {degrees}"
// TODO: Libere a memória e imprima "Freed"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos da Programação Modular
Arquivos de cabeçalhoProteções de inclusãoArquivos-fonteFunções estáticasRevisão: calculadora modular4Encapsulamento
Conceito de ponteiros opacosDefinindo structs opacasGetters e settersValidação em settersRevisão: caixa secreta2Objetos e métodos
Structs como objetosO ponteiro 'Self'Correção de constPonteiro vs. valorMétodos auxiliaresRevisão: Gerenciador de pontos5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de vida do objeto
Padrão de construtorPadrão de destrutorInicialização da pilhaCópia profundaRevisão: wrapper de String6Herança por composição
Incorporação de structRegra do primeiro membroAcessando membros do tipo paiUpcastingRevisão: hierarquia de formas9Projeto: Desenhador de Formas
Visão geral do projetoImplementação do círculoImplementação do retânguloUso polimórficoContêiner de formasPratique por conta própria: Compilador de C online