Упражнение. Использование структур
Иногда требуется представить коллекцию полей в одной структуре. Например, если необходимо написать программу для расчета зарплаты, потребуется использовать структуру данных для сотрудника. В 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}]