Реализация скрытия данных
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 33 из 70.
Скрытие данных выходит за рамки простого использования полей private. Речь идет о проектировании вашего класса таким образом, чтобы детали внутренней реализации были полностью невидимы для внешнего кода, предоставляя только то, что необходимо, через чистый публичный интерфейс.
Рассмотрим класс, который отслеживает пароль пользователя. Вы не хотите, чтобы кто-либо имел доступ к исходному паролю — даже для чтения:
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}Обратите внимание, что нет способа получить пароль или даже хеш. Внешний код может только устанавливать пароль или проверять его — внутренний механизм хранения полностью скрыт. Метод HashPassword также является приватным, поскольку это деталь реализации, которая может измениться, не влияя на то, как используется класс.
Этот подход защищает ваш класс от неправильного использования и дает вам свободу изменять внутреннюю реализацию позже. Если вы решите использовать другой алгоритм хеширования, внешний код не сломается, так как ничто не зависело от того, как хранился пароль.
Задание
ЛегкоДавайте создадим систему безопасного кошелька, которая демонстрирует истинное сокрытие данных. Вы создадите цифровой кошелек, в котором баланс полностью защищен от прямого доступа — внешний код может только вносить средства, снимать их и проверять возможность транзакций, но никогда не сможет увидеть или изменить фактический баланс напрямую.
Вы организуете свой код в двух файлах:
Wallet.cs: Создайте классWalletв пространстве именFinance, который хранит свой баланс полностью скрытым. Кошелек должен иметь:- Закрытое поле (private field) для хранения баланса (decimal)
- Закрытый метод (private method)
FormatBalance(), который возвращает баланс, отформатированный как валютная строка, например"$123.45"— это внутренний помощник, который не должен быть доступен извне - Открытый метод (public method)
Deposit(decimal amount), который добавляет сумму к балансу только в том случае, если сумма положительна, возвращаяtrueв случае успеха иfalseв противном случае - Открытый метод (public method)
Withdraw(decimal amount), который вычитает сумму из баланса только в том случае, если сумма положительна и не превышает текущий баланс, возвращаяtrueв случае успеха иfalseв противном случае - Открытый метод (public method)
CanAfford(decimal amount), который возвращаетtrue, если баланс больше или равен сумме, иfalseв противном случае - Открытый метод (public method)
GetStatement(), который внутренне использует закрытый методFormatBalance()и возвращает"Current balance: {formatted balance}"
Program.cs: В основном файле создайте экземплярWalletи выполните серию транзакций, используя входные значения. Покажите, как кошелек защищает свое внутреннее состояние, предоставляя при этом полезную функциональность через свой открытый интерфейс.
Вы получите три входных значения:
- Сумма начального депозита (decimal)
- Сумма для попытки снятия (decimal)
- Сумма для проверки возможности оплаты (decimal)
Выведите результат в следующем формате:
Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}Например, если входные данные равны 100.50, 30.25 и 80.00, вывод должен быть следующим:
Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: FalseКлючевая идея здесь заключается в том, что внешний код никогда напрямую не касается поля баланса. Даже отформатированная строка баланса поступает через открытый метод, который внутренне использует закрытый вспомогательный метод. Это и есть сокрытие данных — детали реализации полностью невидимы, и вы вольны изменять способ хранения или форматирования баланса, не ломая код, использующий ваш кошелек!
Шпаргалка
Скрытие данных означает проектирование классов таким образом, чтобы детали внутренней реализации были полностью невидимы для внешнего кода, предоставляя только необходимое через чистый публичный интерфейс.
Используйте поля private для хранения конфиденциальных данных и обеспечьте контролируемый доступ через методы public:
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}Ключевые принципы:
- Нет прямого доступа к внутренним данным (отсутствие геттеров для конфиденциальных полей)
- Приватные вспомогательные методы для деталей реализации
- Публичные методы предоставляют только контролируемые операции
- Механизм внутреннего хранения полностью скрыт
Это защищает класс от неправильного использования и позволяет изменять внутреннюю реализацию, не нарушая работу внешнего кода.
Попробуйте сами
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Считать входные значения
decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
decimal checkAmount = Convert.ToDecimal(Console.ReadLine());
// TODO: Создать экземпляр Wallet
// TODO: Выполнить пополнение и вывести результат в формате: "Deposit {amount}: {True/False}"
// TODO: Вывести выписку, используя GetStatement()
// TODO: Выполнить снятие и вывести результат в формате: "Withdraw {amount}: {True/False}"
// TODO: Вывести выписку, используя GetStatement()
// TODO: Проверить доступность средств и вывести результат в формате: "Can afford {amount}: {True/False}"
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методов