Menu
Coddy logo textTech

Реализация скрытия данных

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

Этот подход защищает ваш класс от неправильного использования и дает вам свободу изменять внутреннюю реализацию позже. Если вы решите использовать другой алгоритм хеширования, внешний код не сломается, так как ничто не зависело от того, как хранился пароль.

challenge icon

Задание

Легко

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

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

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

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

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