Сокрытие данных
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 36 из 104.
Скрытие информации выходит за рамки простого использования спецификаторов доступа. Это принцип проектирования, при котором вы скрываете не только данные, но и детали реализации того, как ваш класс работает внутри.
Цель состоит в том, чтобы предоставить пользователям вашего класса только то, что им необходимо знать, скрывая все остальное. Это позволяет изменять внутреннюю реализацию, не затрагивая код, который использует ваш класс:
class Temperature {
double kelvin; // Внутреннее представление — скрытая деталь
public:
void setCelsius(double c) {
kelvin = c + 273.15; // Преобразование скрыто от пользователя
}
double getCelsius() const {
return kelvin - 273.15;
}
double getFahrenheit() const {
return (kelvin - 273.15) * 9.0/5.0 + 32;
}
};Пользователи этого класса не знают (и им не нужно знать), что температура хранится внутри в Кельвинах. Если позже вы решите хранить её в Цельсиях, вы измените только внутреннюю реализацию — публичный интерфейс останется неизменным.
Это разделение между интерфейсом и реализацией дает несколько преимуществ: оно снижает сложность для пользователей вашего класса, предотвращает случайное неправильное использование внутренних данных и делает ваш код более простым в обслуживании. Когда внутренние детали скрыты, вы можете свободно оптимизировать или проводить рефакторинг, не нарушая работу существующего кода, который зависит от вашего класса.
Задание
ЛегкоДавайте создадим класс Distance, который демонстрирует сокрытие информации, сохраняя расстояния внутри в сантиметрах, предоставляя при этом чистый интерфейс для работы с различными единицами измерения. Пользователям вашего класса не нужно будет знать (или беспокоиться) о том, как расстояние хранится внутри — они просто будут работать с той единицей измерения, которая им удобна.
Для организации кода вы создадите два файла:
Distance.h: Определите классDistance, который скрывает свое внутреннее представление, предоставляя гибкий публичный интерфейс. Внутри храните расстояние в сантиметрах (в видеdouble) — это ваша скрытая деталь реализации. Ваш публичный интерфейс должен позволять пользователям:- Устанавливать расстояние с помощью
setMeters(double m),setCentimeters(double cm)илиsetKilometers(double km) - Получать расстояние с помощью
getMeters(),getCentimeters()илиgetKilometers()— все они должны быть методами const - Складывать два расстояния с помощью
add(const Distance& other), который возвращает новый объектDistance
Включите конструктор по умолчанию, который инициализирует расстояние нулем. Логика преобразования должна быть скрыта внутри ваших методов — пользователи просто вызывают
setKilometers(5)иgetMeters(), не беспокоясь о внутреннем формате хранения.- Устанавливать расстояние с помощью
main.cpp: Продемонстрируйте, что пользователи могут работать с вашим классом, не зная внутреннего представления. Считайте два значения расстояния из входных данных: первое в метрах, второе в километрах. Затем:- Создайте объект
Distanceи установите его значение, используя метры - Выведите
"In meters: <value> m" - Выведите
"In centimeters: <value> cm" - Выведите
"In kilometers: <value> km" - Создайте второй объект
Distanceи установите его значение, используя километры - Сложите два расстояния и сохраните результат
- Выведите
"Combined in meters: <value> m"
- Создайте объект
Прелесть сокрытия информации заключается в том, что позже вы сможете изменить внутреннее хранилище с сантиметров на миллиметры (или на что-то другое), не меняя того, как пользователи взаимодействуют с вашим классом. Публичный интерфейс остается стабильным, даже если реализация меняется.
Форматируйте все выходные значения с двумя знаками после запятой, используя std::fixed и std::setprecision(2) из <iomanip>. Преобразуйте входные строки в числа с плавающей точкой с помощью std::stod().
Справочник по преобразованию: 1 метр = 100 сантиметров, 1 километр = 1000 метров.
Шпаргалка
Скрытие информации — это принцип проектирования, при котором вы скрываете детали реализации того, как ваш класс работает внутри, предоставляя только то, что пользователям необходимо знать.
Это позволяет изменять внутреннюю реализацию, не затрагивая код, использующий ваш класс:
class Temperature {
double kelvin; // Внутреннее представление — скрытая деталь
public:
void setCelsius(double c) {
kelvin = c + 273.15; // Преобразование скрыто от пользователя
}
double getCelsius() const {
return kelvin - 273.15;
}
double getFahrenheit() const {
return (kelvin - 273.15) * 9.0/5.0 + 32;
}
};Пользователи не знают (и им не нужно знать), что температура хранится внутри в Кельвинах. Если вы позже измените формат хранения, изменится только приватная реализация — публичный интерфейс останется прежним.
Преимущества скрытия информации:
- Снижает сложность для пользователей вашего класса
- Предотвращает случайное неправильное использование внутренних данных
- Упрощает поддержку и рефакторинг кода
- Позволяет проводить оптимизацию, не ломая существующий код
Попробуйте сами
#include <iostream>
#include <string>
#include <iomanip>
#include "Distance.h"
using namespace std;
int main() {
// Считать входные значения
string metersInput, kmInput;
cin >> metersInput; // Первое значение в метрах
cin >> kmInput; // Второе значение в километрах
// Преобразовать строки в double с помощью `std::stod()`
double metersValue = stod(metersInput);
double kmValue = stod(kmInput);
// Установить форматирование вывода
cout << fixed << setprecision(2);
// TODO: Создать первый объект Distance и установить его, используя значение в метрах
// TODO: Вывести "In meters: <value> m"
// TODO: Вывести "In centimeters: <value> cm"
// TODO: Вывести "In kilometers: <value> km"
// TODO: Создать второй объект Distance и установить его, используя значение в километрах
// TODO: Сложить две дистанции вместе, используя метод `add()`
// TODO: Вывести "Combined in meters: <value> m"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер