Геттеры и сеттеры
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 49 из 107.
Поскольку неэкспортируемые поля не могут быть доступны напрямую извне пакета, вам необходимо предоставить методы, обеспечивающие контролируемый доступ. Их обычно называют геттерами и сеттерами.
В Go соглашение для геттеров простое: используйте имя поля с заглавной первой буквы. Не добавляйте префикс «Get» — просто используйте Balance(), а не GetBalance():
type BankAccount struct {
balance int // неэкспортируемое
}
// Геттер — возвращает значение
func (b *BankAccount) Balance() int {
return b.balance
}
// Сеттер — изменяет значение с валидацией
func (b *BankAccount) SetBalance(amount int) {
if amount >= 0 {
b.balance = amount
}
}
Настоящая мощь сеттеров заключается в добавлении валидации или бизнес-логики. Вместо того чтобы позволять внешнему коду устанавливать любое значение напрямую, вы контролируете то, что разрешено:
func (b *BankAccount) Deposit(amount int) bool {
if amount <= 0 {
return false
}
b.balance += amount
return true
}
Этот метод Deposit действует как специализированный сеттер, который обеспечивает соблюдение правил — отрицательные депозиты не допускаются. Внешний код должен обращаться через ваши методы, гарантируя, что структура всегда остается в валидном состоянии. Это инкапсуляция в действии: скрытие данных при предоставлении контролируемого поведения.
Задание
ЛегкоДавайте создадим конвертер температуры, который демонстрирует правильную инкапсуляцию с использованием методов геттеров и сеттеров. Вы создадите структуру, которая хранит температуру внутри в градусах Цельсия, но обеспечивает контролируемый доступ через методы с валидацией.
Вы организуете свой код в двух файлах:
temperature.go: Создайте структуруTemperatureс неэкспортируемым полемcelsius(float64). Реализуйте следующие методы:Celsius()— геттер, который возвращает текущую температуру в градусах ЦельсияSetCelsius(value float64)— сеттер, который принимает значения только не ниже абсолютного нуля (-273.15). Если значение ниже этого порога, температура должна остаться неизменной.Fahrenheit()— возвращает температуру, преобразованную в градусы Фаренгейта по формуле:(celsius * 9/5) + 32SetFahrenheit(value float64)— преобразует входное значение из Фаренгейтов в Цельсии и сохраняет его, но только если полученное значение в Цельсиях не ниже -273.15
NewTemperature(celsius float64) *Temperature, который создает температуру, инициализированную заданным значением (предполагается, что для конструктора вводятся корректные данные).main.go: Считайте значения температуры из входных данных и продемонстрируйте работу методов геттеров/сеттеров. Создайте объект температуры, выведите его значения в обеих единицах измерения, затем попытайтесь установить новые значения и покажите результаты.
Будут предоставлены следующие входные данные:
- Строка 1: Начальное значение в Цельсиях
- Строка 2: Новое значение в Цельсиях для установки
- Строка 3: Новое значение в Фаренгейтах для установки
Форматируйте все выводы температуры с двумя знаками после запятой. Выведите следующую последовательность:
- После создания:
C: [celsius] F: [fahrenheit] - После установки Цельсия:
C: [celsius] F: [fahrenheit] - После установки Фаренгейта:
C: [celsius] F: [fahrenheit]
Например, при вводе 25, -300 и 212, ваш вывод должен быть:
C: 25.00 F: 77.00
C: 25.00 F: 77.00
C: 100.00 F: 212.00Обратите внимание, что вторая строка показывает неизменные значения, потому что -300 ниже абсолютного нуля, поэтому сеттер отклонил его. Третья строка показывает температуру после успешной установки 212°F (что соответствует 100°C). В этом и заключается сила сеттеров — они обеспечивают соблюдение правил, которые поддерживают корректность ваших данных.
Шпаргалка
Неэкспортируемые поля требуют методов getter (геттер) и setter (сеттер) для контролируемого доступа извне пакета.
Соглашение о геттерах
В Go геттеры используют имя поля с заглавной первой буквы. Не добавляйте префикс "Get":
type BankAccount struct {
balance int // неэкспортируемое
}
// Getter - возвращает значение
func (b *BankAccount) Balance() int {
return b.balance
}Сеттер с валидацией
Сеттеры изменяют значения и могут включать валидацию или бизнес-логику:
// Setter - изменяет значение с валидацией
func (b *BankAccount) SetBalance(amount int) {
if amount >= 0 {
b.balance = amount
}
}Специализированные сеттеры
Создавайте методы, которые обеспечивают соблюдение определенных правил вместо того, чтобы разрешать прямое присваивание значений:
func (b *BankAccount) Deposit(amount int) bool {
if amount <= 0 {
return false
}
b.balance += amount
return true
}Этот подход обеспечивает инкапсуляцию: скрытие данных при предоставлении контролируемого поведения, что поддерживает структуры в валидном состоянии.
Попробуйте сами
package main
import (
"fmt"
)
func main() {
// Считать входные значения
var initialCelsius float64
var newCelsius float64
var newFahrenheit float64
fmt.Scanln(&initialCelsius)
fmt.Scanln(&newCelsius)
fmt.Scanln(&newFahrenheit)
// TODO: Создать новый объект Temperature, используя конструктор
// TODO: Вывести начальные значения в формате "C: [celsius] F: [fahrenheit]" с 2 знаками после запятой
// TODO: Попытаться установить новое значение Цельсия и вывести результат
// TODO: Попытаться установить новое значение Фаренгейта и вывести результат
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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