Menu
Coddy logo textTech

Валидация в сеттерах

Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 20 из 61.

В предыдущем уроке мы создали базовые сеттеры, которые просто присваивают значения. Но при этом упускается ключевое преимущество инкапсуляции: возможность защитить ваши данные путем добавления логики валидации.

Рассмотрим Counter, который никогда не должен становиться отрицательным. Без инкапсуляции любой мог бы написать c->value = -50 и повредить состояние. С помощью сеттера вы контролируете, какие значения допустимы:

// counter.c
void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // Игнорировать некорректное значение
    }
    c->value = new_value;
}

Теперь отрицательные значения просто игнорируются.

Вы также можете возвращать код состояния для индикации успеха или ошибки:

int counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return 0;  // Ошибка
    }
    c->value = new_value;
    return 1;  // Успех
}

Этот паттерн позволяет вызывающей стороне узнать, была ли операция успешной. Ключевая идея заключается в том, что сеттеры — это не просто присваивание, это стражи, которые обеспечивают соблюдение правил относительно того, каким может быть состояние вашего объекта. В этом и заключается истинная сила инкапсуляции: ваш объект сохраняет свою целостность независимо от того, что пытается сделать внешний код.

challenge icon

Задание

Легко

Давайте создадим модуль Temperature, который защищает свои данные с помощью валидации в сеттере. Вы создадите трекер температуры, который принимает только значения в пределах допустимого диапазона, демонстрируя, как сеттеры выступают в роли «стражей» состояния вашего объекта.

Вам нужно создать три файла:

  • temperature.h: Ваш публичный интерфейс, использующий паттерн непрозрачного указателя (opaque pointer). Объявите тип Temperature только с помощью опережающего объявления (forward declaration). Объявите следующие функции:
    • create_temperature — принимает целое число для начальных градусов и возвращает указатель на Temperature
    • free_temperature — освобождает выделенную память
    • temp_get_degrees — геттер, возвращающий текущее значение температуры
    • temp_set_degrees — сеттер, возвращающий int (1 в случае успеха, 0 в случае неудачи)
    Используйте include guards с символом TEMPERATURE_H.
  • temperature.c: Определите саму структуру struct Temperature со скрытым членом int degrees. Реализуйте все функции. Ключевым моментом является ваш сеттер — он должен принимать значения только в диапазоне от -50 до 150 (включительно). Если значение выходит за пределы этого диапазона, верните 0 и оставьте температуру без изменений. Если значение допустимо, обновите его и верните 1.
  • main.c: Продемонстрируйте работу валидации, попытавшись установить как допустимую, так и недопустимую температуру, показывая, что недопустимые значения отклоняются, а допустимые — принимаются.

Вы получите три входных значения: начальную температуру, допустимую температуру для установки (в пределах диапазона) и недопустимую температуру для попытки установки (вне диапазона).

В вашем основном файле создайте объект Temperature с начальным значением и выведите его. Затем попытайтесь установить недопустимую температуру — так как она должна быть отклонена, выведите результат и покажите, что значение осталось неизменным. Наконец, установите допустимую температуру, выведите результат успеха и покажите обновленное значение. Освободите память и подтвердите очистку.

Выводите данные в следующем формате:

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

Например, при входных данных 25, 100 и 200 вывод будет следующим:

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

Это демонстрирует истинную силу инкапсуляции — ваш сеттер обеспечивает соблюдение правил относительно того, что является допустимой температурой, защищая целостность объекта независимо от того, какие значения пытается присвоить внешний код.

Попробуйте сами

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

int main() {
    // Считать входные данные
    int initial, valid_temp, invalid_temp;
    scanf("%d", &initial);
    scanf("%d", &valid_temp);
    scanf("%d", &invalid_temp);
    
    // TODO: Создать Temperature с начальным значением
    
    // TODO: Вывести начальную температуру
    // Формат: "Initial: {degrees}"
    
    // TODO: Попытаться установить недопустимую температуру
    // Вывести результат и показать, что значение осталось неизменным
    // Формат: "Set {invalid}: {0 or 1}"
    // Формат: "After invalid: {degrees}"
    
    // TODO: Установить допустимую температуру
    // Вывести результат успешного выполнения и показать обновленное значение
    // Формат: "Set {valid}: {0 or 1}"
    // Формат: "After valid: {degrees}"
    
    // TODO: Освободить память и вывести "Freed"
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming