Упражнение. Использование структур

Завершено

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

Структура в Go — еще один тип данных, который может содержать ноль или более полей произвольных типов и представляет их как единую сущность.

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

Объявление и инициализация структуры

Чтобы объявить структуру, необходимо использовать struct ключевое слово вместе со списком полей и их типами, которые требуется иметь новому типу данных. Например, чтобы определить структуру для сотрудника, можно использовать следующий код:

type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

Затем можно объявить переменную с типом новой структуры, как и для других типов:

var john Employee

Если вы хотите одновременно объявить и инициализировать переменную, это можно сделать следующим образом:

employee := Employee{1001, "John", "Doe", "Doe's Street"}

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

employee := Employee{LastName: "Doe", FirstName: "John"}

Обратите внимание, что порядок присваивания значений полям в предыдущей инструкции не имеет значения. Также неважно, что значения других полей не указаны. Go присвоит этим полям значения по умолчанию в зависимости от типов данных полей.

Для доступа к отдельным полям используется точка (.), как показано в следующем примере:

employee.ID = 1001
fmt.Println(employee.FirstName)

Наконец, с помощью оператора & можно получить указатель на структуру, как показано в следующем коде:

package main

import "fmt"

type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

func main() {
    employee := Employee{LastName: "Doe", FirstName: "John"}
    fmt.Println(employee)
    employeeCopy := &employee
    employeeCopy.FirstName = "David"
    fmt.Println(employee)
}

При выполнении приведенного выше кода получается следующий результат:

{0 John Doe }
{0 David Doe }

Обратите внимание, что при использовании указателей структура становится изменяемой.

Вложенные структуры

В Go можно указывать одну структуру внутри другой. Бывают случаи, когда необходимо уменьшить количество повторений и повторно использовать общую структуру. Например, предположим, что необходимо выполнить рефакторинг предыдущего кода, чтобы создать тип данных для сотрудника и другой тип данных для подрядчика. Можно создать структуру Person, содержащую общие поля, как показано в следующем примере:

type Person struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

Затем можно объявить другие типы, содержащие структуру Person в качестве вложенной структуры, например Employee и Contractor. Чтобы включить одну структуру в состав другой, создайте новое поле, как показано в следующем примере:

type Employee struct {
    Information Person
    ManagerID   int
}

Но чтобы сослаться на поле из структуры Person, необходимо включить поле Information из переменной сотрудника, как показано в следующем примере:

var employee Employee
employee.Information.FirstName = "John"

Такой рефакторинг кода приведет к тому, что код перестанет работать. Кроме того, можно включить новое поле с тем же именем структуры, которую вы внедряете, как в следующем примере:

type Employee struct {
    Person
    ManagerID int
}

В качестве примера можно использовать следующий код:

package main

import "fmt"

type Person struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

type Employee struct {
    Person
    ManagerID int
}

type Contractor struct {
    Person
    CompanyID int
}

func main() {
    employee := Employee{
        Person: Person{
            FirstName: "John",
        },
    }
    employee.LastName = "Doe"
    fmt.Println(employee.FirstName)
}

Обратите внимание, что для доступа к полю FirstName из структуры Employee не нужно указывать поле Person, так как все поля этой структуры являются вложенными автоматически. Но при инициализации структуры необходимо указать, какому полю вы хотите присвоить значение.

Кодирование и декодирование структур с помощью JSON

Наконец, структуры можно использовать для кодирования и декодирования данных в JSON. Go обладает прекрасной поддержкой формата JSON, и она уже включена в пакеты стандартных библиотек.

Также можно выполнять такие действия, как изменение имени поля структуры. Например, предположим, что вы хотите, чтобы в выходных данных JSON отображалось не FirstName, а просто name, или хотите пропустить пустые поля. Вы можете использовать теги полей, как показано в следующем примере:

type Person struct {
    ID        int    
    FirstName string `json:"name"`
    LastName  string
    Address   string `json:"address,omitempty"`
}

Чтобы закодировать структуру в JSON, используется функция json.Marshal. А чтобы декодировать строку JSON в структуру данных, используется функция json.Unmarshal. Ниже приведен пример, в котором все это сведено вместе: массив сотрудников кодируется в JSON, а результат декодируется в новую переменную:

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    ID        int
    FirstName string `json:"name"`
    LastName  string
    Address   string `json:"address,omitempty"`
}

type Employee struct {
    Person
    ManagerID int
}

type Contractor struct {
    Person
    CompanyID int
}

func main() {
    employees := []Employee{
        Employee{
            Person: Person{
                LastName: "Doe", FirstName: "John",
            },
        },
        Employee{
            Person: Person{
                LastName: "Campbell", FirstName: "David",
            },
        },
    }

    data, _ := json.Marshal(employees)
    fmt.Printf("%s\n", data)

    var decoded []Employee
    json.Unmarshal(data, &decoded)
    fmt.Printf("%v", decoded)
}

При выполнении приведенного выше кода получается следующий результат:

[{"ID":0,"name":"John","LastName":"Doe","ManagerID":0},{"ID":0,"name":"David","LastName":"Campbell","ManagerID":0}]
[{{0 John Doe } 0} {{0 David Campbell } 0}]