Экспортируемые и неэкспортируемые поля
Часть раздела Объектно-ориентированное программирование путешествия по 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 реализует сокрытие данных — основополагающий принцип инкапсуляции в объектно-ориентированном программировании.
Задание
ЛегкоДавайте создадим систему банковских счетов, демонстрирующую инкапсуляцию с помощью экспортируемых и неэкспортируемых полей. Вы создадите пакет, который защищает конфиденциальные финансовые данные, предоставляя безопасную общедоступную информацию.
Вы организуете код в двух файлах:
account.go: Создайте структуруBankAccountв пакетеmain, моделирующую реальный банковский счет с надлежащей защитой данных:HolderName(экспортируемое) — имя владельца счета, безопасное для публичного отображенияAccountType(экспортируемое) — тип счета (например, "Savings", "Checking")balance(неэкспортируемое) — фактический баланс, который должен быть защищенpin(неэкспортируемое) — PIN-код счета, highly sensitive data
NewBankAccount, которая принимает имя владельца, тип счета, initial balance и PIN, а затем возвращает указатель на новыйBankAccount. Также добавьте methodGetPublicInfo() 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
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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Потренируйтесь самостоятельно: Онлайн-компилятор Go