Экспортируемые и неэкспортируемые поля
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 47 из 107.
В Go инкапсуляция на уровне struct контролируется регистром имен полей. Это простое правило определяет, можно ли получить доступ к полям извне пакета, в котором определена struct.
Поля, начинающиеся с заглавной буквы, являются экспортируемыми — они доступны из любого пакета. Поля, начинающиеся со строчной буквы, являются неэкспортируемыми — они доступны только внутри того же пакета:
// В пакете "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 нельзя прочитать или изменить напрямую извне пакета, что заставляет другой код использовать предоставляемые вами методы. Это подход Go к реализации сокрытия данных — основного принципа инкапсуляции в объектно-ориентированном программировании.
Задание
ЛегкоДавайте создадим систему банковских счетов, которая демонстрирует инкапсуляцию через экспортируемые и неэкспортируемые поля. Вы создадите пакет, который защищает конфиденциальные финансовые данные, предоставляя при этом безопасную публичную информацию.
Вы организуете свой код в двух файлах:
account.go: Создайте структуруBankAccountв пакетеmain, которая моделирует реальный банковский счет с надлежащей защитой данных:HolderName(экспортируемое) — имя владельца счета, которое можно безопасно отображать публичноAccountType(экспортируемое) — тип счета (например, "Savings", "Checking")balance(неэкспортируемое) — фактический баланс, который должен быть защищенpin(неэкспортируемое) — PIN-код счета, строго конфиденциальные данные
NewBankAccount, которая принимает имя владельца, тип счета, начальный баланс и PIN-код, а затем возвращает указатель на новыйBankAccount. Также добавьте методGetPublicInfo() string, который возвращает строку в формате:Account: [HolderName] ([AccountType])main.go: Считайте данные счета из входных данных, создайтеBankAccountс помощью конструктора и продемонстрируйте инкапсуляцию. Выведите публичную информацию с помощью метода, а затем напрямую обратитесь к каждому экспортируемому полю и выведите их на отдельных строках. Неэкспортируемые поля (balanceиpin) защищены — вы можете установить их только через конструктор.
Будут предоставлены следующие входные данные:
- Строка 1: Имя владельца счета
- Строка 2: Тип счета
- Строка 3: Начальный баланс (в виде числа с плавающей точкой)
- Строка 4: PIN-код
Например, для входных данных Alice Johnson, Savings, 5000.50 и 1234 ваш вывод должен быть следующим:
Account: Alice Johnson (Savings)
Holder: Alice Johnson
Type: SavingsОбратите внимание, что баланс и PIN-код никогда не появляются в выводе — они надежно скрыты внутри структуры и доступны только внутри пакета. Это и есть инкапсуляция в действии: конфиденциальные данные защищены, в то время как публичная информация остается доступной.
Шпаргалка
В Go **инкапсуляция** управляется регистром имен полей в структурах:
- **Заглавные** поля являются *экспортируемыми* (доступны из любого пакета)
- **Строчные** поля являются *неэкспортируемыми* (доступны только внутри того же пакета)
type User struct {
Name string // Экспортируемое — доступно везде
Email string // Экспортируемое — доступно везде
password string // Неэкспортируемое — доступно только в том же пакете
age int // Неэкспортируемое — доступно только в том же пакете
}
При использовании структуры из другого пакета доступны только экспортируемые поля:
import "user"
func main() {
u := user.User{
Name: "Alice", // работает
Email: "a@mail.com", // работает
// password: "secret", // ошибка компиляции: неизвестное поле
}
fmt.Println(u.Name) // работает
// fmt.Println(u.password) // ошибка компиляции: нельзя ссылаться на неэкспортируемое поле
}
Этот механизм защищает конфиденциальные данные и детали внутренней реализации, заставляя внешний код использовать предоставленные методы для доступа.
Попробуйте сами
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
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool