Сокрытие данных
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 36 из 104.
Сокрытие информации выходит за рамки простого использования спецификаторов доступа. Это принцип проектирования, согласно которому вы скрываете не только данные, но и детали того, как ваш class работает внутри.
Цель состоит в том, чтобы предоставить только то, что пользователям вашего class необходимо знать, оставив всё остальное скрытым. Это позволяет изменять внутреннюю реализацию, не затрагивая код, который использует ваш class:
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;
}
};Пользователи этого class не знают (и им не нужно знать), что температура внутри хранится в кельвинах. Если позже вы решите хранить её в градусах Цельсия, вам потребуется изменить только внутреннюю implementation — публичный интерфейс останется неизменным.
Это разделение между интерфейсом и реализацией предоставляет несколько преимуществ: оно снижает сложность для пользователей вашего класса, предотвращает случайное неправильное использование внутренних данных и упрощает сопровождение вашего кода. Когда внутренние детали скрыты, вы можете оптимизировать или переработать код, не нарушая существующий код, который зависит от вашего класса.
Задание
ЛегкоДавайте создадим class Distance, демонстрирующий сокрытие информации: расстояния будут внутренне храниться в centimeters, а пользователям будет предоставлен удобный интерфейс для работы с разными единицами измерения. Пользователям вашего class не потребуется знать (или беспокоиться о том), как расстояние хранится internally: они просто будут работать с удобной для них единицей измерения.
Вы создадите два файла, чтобы организовать код:
Distance.h: определите classDistance, который скрывает своё внутреннее представление, предоставляя гибкий public-интерфейс. Внутри храните расстояние в centimeters (какdouble). Это ваша скрытая деталь реализации. Ваш public-интерфейс должен позволять пользователям:- Задавать расстояние с помощью
setMeters(double m),setCentimeters(double cm)илиsetKilometers(double km) - Получать расстояние с помощью
getMeters(),getCentimeters()илиgetKilometers(): все они должны быть const-методами - Складывать два расстояния с помощью
add(const Distance& other), который возвращает новый объектDistance
Включите Default constructor, который initializes расстояние нулём. Логика преобразования должна быть скрыта внутри ваших методов: пользователи просто вызывают
setKilometers(5)иgetMeters(), не беспокоясь о внутреннем формате хранения.- Задавать расстояние с помощью
main.cpp: продемонстрируйте, что пользователи могут работать с вашим class, не зная его внутреннего представления. Считайте из input два значения расстояния: first — в метрах, second — в kilometers. Затем:- Создайте объект
Distanceи задайте его, используя значение в метрах - Выведите
"In meters: <value> m" - Выведите
"In centimeters: <value> cm" - Выведите
"In kilometers: <value> km" - Создайте второй объект
Distanceи задайте его, используя значение в kilometers - Сложите два расстояния и сохраните результат
- Выведите
"Combined in meters: <value> m"
- Создайте объект
Преимущество сокрытия информации заключается в том, что позднее вы сможете изменить внутреннее хранение с centimeters на миллиметры (или на что-то другое), не меняя способ взаимодействия пользователей с вашим class. Public-интерфейс останется неизменным, даже если реализация изменится.
Форматируйте все выходные значения с двумя знаками после запятой, используя std::fixed и std::setprecision(2) из <iomanip>. Преобразуйте input-строки в значения типа double с помощью std::stod().
Справочник преобразований: 1 метр = 100 centimeters, 1 kilometer = 1000 метров.
Попробуйте сами
#include <iostream>
#include <string>
#include <iomanip>
#include "Distance.h"
using namespace std;
int main() {
// Читаем входные значения
string metersInput, kmInput;
cin >> metersInput; // Первое значение в метрах
cin >> kmInput; // Second value in kilometers
// Преобразуем строки в double с помощью std::stod()
double metersValue = stod(metersInput);
double kmValue = stod(kmInput);
// Устанавливаем форматирование вывода
cout << fixed << setprecision(2);
// TODO: Создать первый объект Distance и задать его значением в метрах
// TODO: Вывести "In meters: <value> m"
// TODO: Print "In centimeters: <value> cm"
// TODO: Print "In kilometers: <value> km"
// TODO: Создать второй объект Distance и задать его значением в километрах
// TODO: Сложить два расстояния с помощью метода add()
// TODO: Print "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Итоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++