Основы встраивания структур
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 35 из 107.
Встраивание структур — это механизм Go для включения одного типа структуры в другой. Вместо имени поля вы просто указываете тип, и Go делает fields встроенной структуры доступными во внешней структуре.
Вот базовый синтаксис:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // встроенный - без имени поля
}
Когда вы встраиваете Address в Person, вы можете обращаться к встроенным полям напрямую через внешнюю структуру:
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London - прямой доступ
fmt.Println(p.Country) // UK - прямой доступ
fmt.Println(p.Address.City) // также работает
}
Обрати внимание, как p.City работает без явной записи p.Address.City. Go автоматически продвигает поля встроенной структуры, делая их доступными так, как будто они принадлежат внешней структуре. При необходимости к ним по-прежнему можно обращаться через имя встроенного типа.
Это отличается от обычных вложенных структур, где для доступа к внутренним fields всегда необходимо использовать имя field. Embedding создаёт более плоский и удобный API, сохраняя при этом логическую группировку связанных данных.
Задание
ЛегкоДавайте создадим систему регистрации транспортных средств, которая использует встраивание структур для создания чистой, организованной модели данных. Вы отделите контактную информацию от сведений о транспортном средстве, сохранив возможность direct доступа ко всем fields благодаря встраиванию.
Вы организуете свой код в двух файлах:
vehicle.go: создайте две структуры, которые взаимодействуют благодаря встраиванию:- структуру
ContactInfoс fieldsOwnerNameиPhone(обе строкового типа) - структуру
Vehicle, которая встраиваетContactInfo(без имени field) и добавляет собственные fieldsMake,ModelиYear
SummaryдляVehicle, который возвращает formatted строку, содержащую всю информацию о транспортном средстве и контактах.- структуру
main.go: считайте сведения о транспортном средстве и owner из input, создайтеVehicleinstance со встроеннымContactInfoи продемонстрируйте доступ к встроенным fields как direct, так и через имя встроенного типа. В конце выведите сводку о транспортном средстве.
Будут предоставлены следующие входные данные:
- Строка 1: имя owner
- Строка 2: номер телефона
- Строка 3: Make транспортного средства
- Строка 4: Model транспортного средства
- Строка 5: год (целое число)
Ваш method Summary должен возвращать строку в следующем формате:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]В основном файле продемонстрируйте продвижение field, выведя:
- имя owner, к которому выполнен direct доступ через Vehicle (например,
v.OwnerName) - номер телефона, к которому выполнен доступ через встроенный тип (например,
v.ContactInfo.Phone) - полную сводку из вашего method
Summary
Например, если заданы Alice Smith, 555-1234, Toyota, Camry и 2023, результатом должен быть:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234Обратите внимание, как работают оба способа доступа: direct доступ благодаря продвижению field и явный доступ через имя встроенного типа, что дает вам гибкость при работе со встроенными данными.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Прочитать имя владельца
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Прочитать номер телефона
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// Прочитать марку автомобиля
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// Прочитать модель автомобиля
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// Прочитать год
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: Создать экземпляр Vehicle со встроенным ContactInfo
// Использовать значения, прочитанные из ввода выше
// TODO: Вывести имя владельца, доступное напрямую на Vehicle (продвижение полей)
// Пример: fmt.Println(v.OwnerName)
// TODO: Вывести телефон, доступный через имя встроенного типа
// Пример: fmt.Println(v.ContactInfo.Phone)
// TODO: Вывести полное резюме, используя метод Summary
// Пример: fmt.Println(v.Summary())
// Удалите эти строки, как только реализуете решение
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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