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