Валидация в сеттерах
Часть раздела 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; // Успех
}Этот паттерн позволяет вызывающей стороне узнать, была ли операция успешной. Ключевая идея заключается в том, что сеттеры — это не просто присваивание, это стражи, которые обеспечивают соблюдение правил относительно того, каким может быть состояние вашего объекта. В этом и заключается истинная сила инкапсуляции: ваш объект сохраняет свою целостность независимо от того, что пытается сделать внешний код.
Задание
ЛегкоДавайте создадим модуль Temperature, который защищает свои данные с помощью валидации в сеттере. Вы создадите трекер температуры, который принимает только значения в пределах допустимого диапазона, демонстрируя, как сеттеры выступают в роли «стражей» состояния вашего объекта.
Вам нужно создать три файла:
temperature.h: Ваш публичный интерфейс, использующий паттерн непрозрачного указателя (opaque pointer). Объявите типTemperatureтолько с помощью опережающего объявления (forward declaration). Объявите следующие функции:create_temperature— принимает целое число для начальных градусов и возвращает указатель на Temperaturefree_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Это демонстрирует истинную силу инкапсуляции — ваш сеттер обеспечивает соблюдение правил относительно того, что является допустимой температурой, защищая целостность объекта независимо от того, какие значения пытается присвоить внешний код.
Шпаргалка
Сеттеры могут включать логику валидации для защиты целостности данных. Вместо простого присваивания значений, они действуют как стражи, которые обеспечивают соблюдение правил относительно допустимых состояний объекта.
Пример базовой валидации — молчаливое отклонение недопустимых входных данных:
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; // Успех
}Этот паттерн позволяет вызывающему коду проверять, была ли операция успешной, что позволяет объектам сохранять собственную целостность независимо от попыток внешнего кода.
Попробуйте сами
#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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга