Скрытие данных
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 34 из 110.
Скрытие информации — это принцип, лежащий в основе инкапсуляции: идея о том, что класс должен скрывать свои внутренние механизмы и предоставлять только то, что необходимо. Вы изучили механику (private fields, геттеры, сеттеры), но настоящая цель — проектирование классов, которые легко использовать и сложно использовать неправильно.
Рассмотрим класс, который управляет паролем пользователя. Внутреннее хранилище и логика хеширования должны быть полностью скрыты:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password'; // Упрощенный пример
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}Внешний код никогда не видит хеш пароля и не знает, как он хранится. Он просто вызывает verifyPassword(). Такое разделение означает, что вы можете полностью изменить алгоритм хеширования, не затрагивая код, использующий этот класс.
Ключевая идея заключается в том, чтобы смотреть на свой класс снаружи внутрь.
Спросите себя: «Что на самом деле нужно тому, кто использует этот класс?» Сделайте доступным только это, а всё остальное оставьте приватным. Это создает четкую границу между тем, что делает ваш класс (его публичный интерфейс), и тем, как он это делает (его приватная реализация).
Задание
ЛегкоДавайте создадим систему безопасного кошелька, которая демонстрирует сокрытие информации в действии. Вы создадите цифровой кошелек, который управляет балансом пользователя и историей транзакций, сохраняя все конфиденциальные внутренние детали полностью скрытыми от внешнего кода.
Вы организуете свой код в двух файлах:
wallet.dart: Создайте классWallet, который безопасно управляет деньгами. Кошелек должен хранить имя владельца публично, но все остальное должно быть скрыто за чистым публичным интерфейсом:- Внутренний баланс и журнал транзакций должны быть приватными — внешний код никогда не должен видеть, как вы храните или отслеживаете деньги
- Приватный метод должен обрабатывать запись транзакций внутри (сохраняя записи вида
"Deposit: $50.0"или"Withdrawal: $30.0") - Предоставьте публичный метод
deposit(double amount), который добавляет деньги (только если сумма положительная) и записывает транзакцию внутри - Предоставьте публичный метод
withdraw(double amount), который снимает деньги только при наличии достаточных средств и положительной сумме, записывая транзакцию внутри. Возвращайтеtrueв случае успеха, в противном случае —false - Предоставьте публичный геттер
balance, который возвращает текущий баланс (доступ только для чтения) - Предоставьте публичный метод
getTransactionCount(), который возвращает количество записанных транзакций (не раскрывая сам список транзакций) - Предоставьте публичный метод
getLastTransaction(), который возвращает строку последней транзакции или"No transactions", если транзакций нет - Включите метод
printSummary(), который отображает статус кошелька
main.dart: Импортируйте ваш кошелек и продемонстрируйте, как сокрытие информации создает чистый и безопасный интерфейс:- Создайте кошелек для владельца
"Sarah" - Внесите депозит
100.0 - Внесите депозит
50.0 - Попытайтесь снять
200.0и выведите"Withdrawal of 200.0: [success/failed]"в зависимости от результата - Снимите
30.0и выведите"Withdrawal of 30.0: [success/failed]" - Вызовите
printSummary() - Выведите
"Transaction count: [count]" - Выведите
"Last transaction: [transaction]"
- Создайте кошелек для владельца
Метод printSummary() должен выводить:
=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]Обратите внимание, как внешний код может взаимодействовать с кошельком через четкие, целенаправленные методы, даже не зная, как хранится баланс или как транзакции отслеживаются внутри. Детали реализации полностью скрыты — вы могли бы изменить способ хранения транзакций, не затрагивая код, использующий кошелек.
Ожидаемый вывод:
Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0Шпаргалка
Скрытие информации — это принцип, лежащий в основе инкапсуляции: сокрытие внутреннего устройства класса и предоставление доступа только к тому, что необходимо.
Проектируйте классы «снаружи внутрь»: открывайте только то, что нужно пользователям, и оставляйте всё остальное приватным. Это создает четкую границу между публичным интерфейсом (что делает класс) и приватной реализацией (как он это делает).
Пример скрытия информации на примере менеджера паролей:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password';
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}Внешний код никогда не видит хеш пароля или механизм хранения — он использует только verifyPassword(). Это позволяет изменять внутреннюю реализацию, не затрагивая внешний код.
Попробуйте сами
import 'wallet.dart';
void main() {
// TODO: Создать кошелек для владельца "Sarah"
// TODO: Внести депозит 100.0
// TODO: Внести депозит 50.0
// TODO: Попытаться снять 200.0 и вывести результат
// Формат: "Withdrawal of 200.0: [success/failed]"
// TODO: Снять 30.0 и вывести результат
// Формат: "Withdrawal of 30.0: [success/failed]"
// TODO: Вызвать printSummary()
// TODO: Вывести количество транзакций
// Формат: "Transaction count: [count]"
// TODO: Вывести последнюю транзакцию
// Формат: "Last transaction: [transaction]"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий