Валидация в сеттерах
Часть раздела 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Это демонстрирует истинную силу инкапсуляции — ваш сеттер обеспечивает соблюдение правил относительно того, что является допустимой температурой, защищая целостность объекта независимо от того, какие значения пытается присвоить внешний код.
Попробуйте сами
#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Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигур