Menu
Coddy logo textTech

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

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

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

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

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, при условии, что методы ведут себя таким же образом.

Этот принцип также применяется к helper functions. Сохраняйте internal logic неэкспортируемой:

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

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

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

challenge icon

Задание

Легко

Давайте создадим secure wallet system, демонстрирующую information hiding. Вы создадите digital wallet, в котором отслеживание внутреннего balance и история транзакций полностью скрыты от внешнего кода: через методы предоставляется только поведение.

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

  • wallet.go: Create структуру Wallet, которая скрывает всё своё внутреннее состояние. Кошелёк должен отслеживать current balance и лимит расходов внутри, но внешний код не должен знать, как они хранятся или представлены. Предоставьте поведение через следующие методы:
    • NewWallet(limit float64) *Wallet — constructor, который creates кошелёк с указанным лимитом расходов и нулевым balance
    • Deposit(amount float64) bool — adds деньги в кошелёк и возвращает true, если операция выполнена успешно (amount должен быть положительным)
    • Spend(amount float64) bool — списывает деньги, если существует достаточный balance AND amount не превышает лимит расходов; возвращает true, если операция выполнена успешно
    • Balance() float64 — возвращает current balance
    • Status() string — возвращает сообщение о состоянии в формате: Balance: $[balance] (Limit: $[limit])
    Сохраните любую helper logic (например, проверку допустимости транзакции) как неэкспортируемые functions или методы. Это internal implementation details, которые могут измениться позже.
  • main.go: Считайте конфигурацию кошелька и транзакции из входных данных, Create кошелёк, выполните операции и отобразите результаты после каждого действия.

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

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

После создания кошелька выведите его initial status. Затем выполните каждую операцию и выведите либо обновлённый status (если операция выполнена успешно), либо 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, несмотря на то что balance достаточен. Кошелёк применяет свои правила внутренним образом, не раскрывая, как именно он принимает эти решения. Внешний код просто вызывает методы и получает результаты, никогда не узнавая внутренние implementation details.

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

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: Perform deposit and print result (status or "Transaction failed")

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

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

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор Go