Menu
Coddy logo textTech

Основы встраивания структур

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

Встраивание struct — это механизм Go для включения одного типа struct в другой. Вместо использования имени поля вы просто указываете тип, и Go продвигает поля встроенной struct во внешнюю структуру.

Вот базовый синтаксис:

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 автоматически продвигает поля встроенной структуры, делая их доступными так, как если бы они принадлежали внешней структуре. Вы все еще можете получить к ним доступ через имя встроенного типа, когда это необходимо.

Это отличается от обычных вложенных структур, где для доступа к внутренним полям вы всегда должны использовать имя поля. Встраивание создает более плоский и удобный API, сохраняя при этом логическую группировку связанных данных.

challenge icon

Задание

Легко

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

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

  • vehicle.go: Создайте две структуры, которые работают вместе через встраивание:
    • Структура ContactInfo с полями OwnerName и Phone (оба типа string)
    • Структура Vehicle, которая встраивает ContactInfo (без имени поля) и добавляет свои собственные поля Make, Model и Year
    Также создайте метод с именем Summary для Vehicle, который возвращает отформатированную строку, содержащую всю информацию о транспортном средстве и контактах.
  • main.go: Считайте данные о транспортном средстве и владельце из входных данных, создайте экземпляр Vehicle со встроенным ContactInfo и продемонстрируйте доступ к встроенным полям как напрямую, так и через имя встроенного типа. В конце выведите сводку о транспортном средстве.

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

  • Строка 1: Имя владельца
  • Строка 2: Номер телефона
  • Строка 3: Марка автомобиля
  • Строка 4: Модель автомобиля
  • Строка 5: Год (целое число)

Ваш метод Summary должен возвращать строку в следующем формате:

[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]

В вашем основном файле продемонстрируйте продвижение полей (field promotion), выведя:

  1. Имя владельца, доступ к которому осуществляется напрямую через Vehicle (например, v.OwnerName)
  2. Телефон, доступ к которому осуществляется через встроенный тип (например, v.ContactInfo.Phone)
  3. Полную сводку из вашего метода Summary

Например, для Alice Smith, 555-1234, Toyota, Camry и 2023 ваш вывод должен быть следующим:

Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234

Обратите внимание, как работают оба способа доступа — прямой доступ через продвижение полей и явный доступ через имя встроенного типа — что дает вам гибкость при работе со встроенными данными.

Шпаргалка

Встраивание структур позволяет включать один тип структуры в другой без указания имени поля. Поля встроенной структуры переносятся (продвигаются) во внешнюю структуру, что делает их доступными напрямую.

Базовый синтаксис

type Address struct {
    City    string
    Country string
}

type Person struct {
    Name string
    Address  // embedded - no field name
}

Доступ к встроенным полям

Вы можете получить доступ к встроенным полям двумя способами:

p := Person{
    Name: "Alice",
    Address: Address{
        City:    "London",
        Country: "UK",
    },
}

// Direct access (field promotion)
fmt.Println(p.City)     // London

// Through embedded type name
fmt.Println(p.Address.City)  // London

Go автоматически продвигает поля встроенной структуры, обеспечивая прямой доступ к ним, как если бы они принадлежали внешней структуре. При необходимости вы все равно можете получить к ним доступ через имя встроенного типа.

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

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
}
quiz iconПроверьте себя

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

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