Валидация в сеттерах
Часть раздела Объектно-ориентированное программирование путешествия по 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; // Успех
}Этот шаблон позволяет вызывающему коду узнать, успешно ли выполнена операция. Главное здесь то, что сеттеры предназначены не только для присваивания: они выступают в роли контролёров, обеспечивающих соблюдение правил, определяющих допустимое состояние вашего объекта. В этом и заключается настоящая сила инкапсуляции: объект сохраняет свою целостность независимо от того, что пытается сделать внешний код.
Задание
ЛегкоДавайте создадим модуль Temperature, который защищает свои данные с помощью проверки в setter. Вы создадите средство отслеживания температуры, принимающее только значения в допустимом диапазоне: это демонстрирует, как setter выступает в роли привратника для состояния вашего объекта.
Вы создадите три файла:
temperature.h: ваш публичный интерфейс с использованием шаблона непрозрачного указателя. Declare типTemperatureтолько с помощью предварительного объявления. Declare следующие функции:create_temperature: принимает целое число, обозначающее initial degrees, и возвращает указатель на Temperaturefree_temperature: освобождает выделенную памятьtemp_get_degrees: getter, возвращающий текущее значение температурыtemp_set_degrees: setter, возвращающийint(1 при успехе, 0 при failure)
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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C