Menu
Coddy logo textTech

Экспортируемые и неэкспортируемые поля

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

В Go инкапсуляция на уровне структуры определяется регистром имён полей. Это простое правило определяет, могут ли поля быть доступны из пакета, в котором определена структура.

Поля, начинающиеся с uppercase letter, являются exported: accessible из любого пакета. Поля, начинающиеся с lowercase letter, являются unexported: доступны только внутри того же пакета:

// В пакете "user"
type User struct {
    Name     string  // Экспортируемый - доступен везде
    Email    string  // Экспортируемый - доступен везде
    password string  // Неэкспортируемый - доступен только в пакете "user"
    age      int     // Неэкспортируемый - доступен только в пакете "user"
}

Когда другой пакет импортирует и использует эту структуру, он может взаимодействовать только с экспортированными полями:

// В пакете "main"
import "user"

func main() {
    u := user.User{
        Name:  "Alice",      // работает
        Email: "a@mail.com", // работает
        // password: "secret", // ошибка компиляции: неизвестное поле
    }
    
    fmt.Println(u.Name)     // работает
    // fmt.Println(u.password) // ошибка компиляции: нельзя ссылаться на неэкспортированное поле
}

Этот механизм защищает конфиденциальные данные и внутренние детали реализации. Поле password нельзя прочитать или изменить directly извне пакета, что вынуждает другой код использовать предоставленные вами методы. Так Go реализует сокрытие данных — основополагающий принцип инкапсуляции в объектно-ориентированном программировании.

challenge icon

Задание

Легко

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

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

  • account.go: Создайте структуру BankAccount в пакете main, моделирующую реальный банковский счет с надлежащей защитой данных:
    • HolderName (экспортируемое) — имя владельца счета, безопасное для публичного отображения
    • AccountType (экспортируемое) — тип счета (например, "Savings", "Checking")
    • balance (неэкспортируемое) — фактический баланс, который должен быть защищен
    • pin (неэкспортируемое) — PIN-код счета, highly sensitive data
    Добавьте constructor function NewBankAccount, которая принимает имя владельца, тип счета, initial balance и PIN, а затем возвращает указатель на новый BankAccount. Также добавьте method GetPublicInfo() string, которая возвращает формат: Account: [HolderName] ([AccountType])
  • main.go: Считайте данные счета из входных данных, создайте BankAccount с помощью constructor и продемонстрируйте инкапсуляцию. Выведите общедоступную информацию с помощью method, затем напрямую получите доступ к каждому экспортируемому полю и выведите их в отдельных строках. Неэкспортируемые поля (balance и pin) защищены. Установить их можно только через constructor.

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

  • Line 1: имя владельца счета
  • Line 2: тип счета
  • Line 3: initial balance (в формате float)
  • Line 4: PIN-код

Например, если заданы Alice Johnson, Savings, 5000.50 и 1234, ваши выходные данные должны быть такими:

Account: Alice Johnson (Savings)
Holder: Alice Johnson
Type: Savings

Обратите внимание, что balance и PIN никогда не появляются в выходных данных: они безопасно скрыты внутри структуры и доступны только внутри пакета. Это инкапсуляция в действии: конфиденциальные данные защищены, а общедоступная информация остается accessible.

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

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Прочитать имя владельца счёта
	holderName, _ := reader.ReadString('\n')
	holderName = strings.TrimSpace(holderName)

	// Прочитать тип счёта
	accountType, _ := reader.ReadString('\n')
	accountType = strings.TrimSpace(accountType)

	// Прочитать начальный баланс
	balanceStr, _ := reader.ReadString('\n')
	balanceStr = strings.TrimSpace(balanceStr)
	balance, _ := strconv.ParseFloat(balanceStr, 64)

	// Прочитать PIN
	pinStr, _ := reader.ReadString('\n')
	pin := strings.TrimSpace(pinStr)

	// TODO: Создать новый BankAccount с помощью конструктора
	// account := NewBankAccount(...)

	// TODO: Вывести публичную информацию с помощью метода GetPublicInfo()

	// TODO: Вывести имя владельца, напрямую обращаясь к экспортированному полю
	// fmt.Println("Holder:", ...)

	// TODO: Вывести тип счёта, напрямую обращаясь к экспортированному полю
	// fmt.Println("Type:", ...)

	// Note: balance и pin неэкспортированы - к ним нельзя обратиться напрямую здесь
	// Они безопасно инкапсулированы внутри структуры

	_ = balance // Удалите эту строку, когда будете использовать balance
	_ = pin     // Удалите эту строку, когда будете использовать pin
}
quiz iconПроверьте себя

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

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

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