Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по 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;
}

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

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

int counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return 0;  // Неудача
    }
    c->value = new_value;
    return 1;  // Успех
}

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

challenge icon

Задание

Легко

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

Вы создадите три файла:

  • temperature.h: ваш публичный интерфейс с использованием шаблона непрозрачного указателя. Declare тип Temperature только с помощью предварительного объявления. Declare следующие функции:
    • create_temperature: принимает целое число, обозначающее initial degrees, и возвращает указатель на Temperature
    • free_temperature: освобождает выделенную память
    • temp_get_degrees: getter, возвращающий текущее значение температуры
    • temp_set_degrees: setter, возвращающий int (1 при успехе, 0 при failure)
    Используйте include guards с символом TEMPERATURE_H.
  • temperature.c: Define фактическую структуру struct Temperature со скрытым членом int degrees. Implement все функции. Ключевую роль играет setter. Он должен accept только значения between -50 и 150 (inclusive). Если значение находится за пределами этого диапазона, верните 0 и leave температуру без изменений. Если значение допустимо, обновите его и верните 1.
  • main.c: продемонстрируйте проверку, пытаясь установить допустимые и invalid температуры, показывая, что invalid значения отклоняются, а допустимые принимаются.

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

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

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

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

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

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

#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}"
    // Format: "After invalid: {degrees}"
    
    // TODO: Установить допустимую температуру
    // Вывести результат успеха и показать обновлённое значение
    // Формат: "Set {valid}: {0 or 1}"
    // Формат: "After valid: {degrees}"
    
    // TODO: Освободить память и вывести "Freed"
    
    return 0;
}
quiz iconПроверьте себя

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

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

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