Menu
Coddy logo textTech

Скрытие данных

Часть раздела Объектно-ориентированное программирование путешествия по 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(). Такое разделение означает, что вы можете полностью изменить алгоритм хеширования, не затрагивая код, использующий этот класс.

Ключевая идея заключается в том, чтобы смотреть на свой класс снаружи внутрь.

Спросите себя: «Что на самом деле нужно тому, кто использует этот класс?» Сделайте доступным только это, а всё остальное оставьте приватным. Это создает четкую границу между тем, что делает ваш класс (его публичный интерфейс), и тем, как он это делает (его приватная реализация).

challenge icon

Задание

Легко

Давайте создадим систему безопасного кошелька, которая демонстрирует сокрытие информации в действии. Вы создадите цифровой кошелек, который управляет балансом пользователя и историей транзакций, сохраняя все конфиденциальные внутренние детали полностью скрытыми от внешнего кода.

Вы организуете свой код в двух файлах:

  • 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]"
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование