Menu
CoddyTech

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

Часть раздела Объектно-ориентированное программирование путешествия по 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: публичный интерфейс с использованием шаблона непрозрачного указателя. Объявите тип Temperature только с помощью предварительного объявления. Объявите следующие функции:
    • create_temperature: принимает целое число начальной температуры и возвращает указатель на Temperature
    • free_temperature: освобождает выделенную память
    • temp_get_degrees: геттер, возвращающий текущее значение температуры
    • temp_set_degrees: сеттер, возвращающий int (1 в случае успеха, 0 в случае ошибки)
    Используйте защиту от повторного включения с символом 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Проверьте себя

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

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C