Menu
Coddy logo textTech

Сокрытие информации в Go

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 50 из 107.

Скрытие информации выходит за рамки простого объявления полей неэкспортируемыми. Речь идет о проектировании ваших типов таким образом, чтобы внешний код знал только то, что ему необходимо знать — и ничего больше.

Цель состоит в том, чтобы предоставить доступ к поведению через методы, сохраняя при этом внутреннюю структуру полностью скрытой. Рассмотрите этот подход:

type Counter struct {
    value    int
    maxLimit int
}

func NewCounter(limit int) *Counter {
    return &Counter{maxLimit: limit}
}

func (c *Counter) Increment() bool {
    if c.value >= c.maxLimit {
        return false
    }
    c.value++
    return true
}

func (c *Counter) Value() int {
    return c.value
}

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

Этот принцип также применим к вспомогательным функциям. Оставляйте внутреннюю логику неэкспортируемой:

// Экспортируемая — часть публичного API
func (c *Counter) Reset() {
    c.value = 0
}

// неэкспортируемая — внутренний помощник
func (c *Counter) isAtLimit() bool {
    return c.value >= c.maxLimit
}

Скрывая isAtLimit(), вы можете свободно изменять или удалять его позже. Скрытие информации создает четкую границу между тем, что ваш пакет обещает внешним пользователям, и тем, как он работает внутри.

challenge icon

Задание

Легко

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

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

  • wallet.go: Создайте структуру Wallet, которая скрывает все свое внутреннее состояние. Кошелек должен внутренне отслеживать текущий баланс и лимит расходов, но внешний код никогда не должен знать, как они хранятся или представлены. Предоставьте поведение через эти методы:
    • NewWallet(limit float64) *Wallet - конструктор, который создает кошелек с заданным лимитом расходов и нулевым балансом
    • Deposit(amount float64) bool - добавляет деньги в кошелек, возвращает true в случае успеха (сумма должна быть положительной)
    • Spend(amount float64) bool - списывает деньги, если баланса достаточно И сумма не превышает лимит расходов, возвращает true в случае успеха
    • Balance() float64 - возвращает текущий баланс
    • Status() string - возвращает сообщение о статусе в формате: Balance: $[balance] (Limit: $[limit])
    Держите любую вспомогательную логику (например, проверку валидности транзакции) в виде неэкспортируемых функций или методов — это детали внутренней реализации, которые могут измениться позже.
  • main.go: Считайте конфигурацию кошелька и транзакции из входных данных, создайте кошелек, выполните операции и выведите результаты после каждого действия.

Будут предоставлены следующие входные данные:

  • Строка 1: Лимит расходов
  • Строка 2: Сумма депозита
  • Строка 3: Сумма первого списания
  • Строка 4: Сумма второго списания

После создания кошелька выведите его начальный статус. Затем выполните каждую операцию и выведите либо обновленный статус (в случае успеха), либо Transaction failed (если операция была отклонена). Форматируйте все денежные суммы с двумя знаками после запятой.

Например, для входных данных 50, 100, 30 и 80 ваш вывод должен быть следующим:

Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failed

Последняя транзакция не удалась, потому что $80 превышает лимит расходов в $50 — даже несмотря на достаточный баланс. Кошелек применяет свои правила внутренне, не раскрывая, как он принимает эти решения. Внешний код просто вызывает методы и получает результаты, никогда не зная деталей внутренней реализации.

Шпаргалка

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

Пример типа со скрытой реализацией:

type Counter struct {
    value    int
    maxLimit int
}

func NewCounter(limit int) *Counter {
    return &Counter{maxLimit: limit}
}

func (c *Counter) Increment() bool {
    if c.value >= c.maxLimit {
        return false
    }
    c.value++
    return true
}

func (c *Counter) Value() int {
    return c.value
}

Внешний код не знает о внутреннем представлении (это может быть int, float64 или что-то другое). Реализация может измениться без влияния на код, который её использует.

Оставляйте вспомогательные функции неэкспортируемыми:

// Экспортируемая — часть публичного API
func (c *Counter) Reset() {
    c.value = 0
}

// неэкспортируемая — внутренний помощник
func (c *Counter) isAtLimit() bool {
    return c.value >= c.maxLimit
}

Скрытие информации создает четкую границу между тем, что ваш пакет обещает внешне, и тем, как он работает внутри.

Попробуйте сами

package main

import (
	"fmt"
)

func main() {
	// Считать входные данные
	var limit float64
	var depositAmount float64
	var spend1 float64
	var spend2 float64

	fmt.Scanln(&limit)
	fmt.Scanln(&depositAmount)
	fmt.Scanln(&spend1)
	fmt.Scanln(&spend2)

	// TODO: Создать новый кошелек с заданным лимитом расходов

	// TODO: Вывести начальный статус

	// TODO: Выполнить пополнение и вывести результат (статус или "Transaction failed")

	// TODO: Выполнить первый расход и вывести результат

	// TODO: Выполнить второй расход и вывести результат
}
quiz iconПроверьте себя

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

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