Files
build-web-application-with-…/ru/02.4.md
2021-06-21 22:01:42 +07:00

215 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2.4 Структуры
## struct
В Go мы можем определять новые типы контейнеров свойств или полей так же, как и в других языках программирования. Например, чтобы описать личность, мы можем создать тип `person`с полями "имя" и "возраст". Мы назовем этот тип `структурой(struct)`:
```Go
type person struct {
name string
age int
}
```
Вот так легко определять `структуру`!
У нас есть два поля:
- `name` - `string`, используется для того, чтобы хранить имя человека.
- `age` - `int`, используется для того, чтобы хранить возраст человека.
Давайте посмотрим, как это использовать:
```Go
type person struct {
name string
age int
}
var P person // p - переменная типа person
P.name = "Astaxie" // присваиваем "Astaxie" полю 'name' переменной p
P.age = 25 // присваиваем 25 полю 'age' переменной p
fmt.Printf("Имя человека - %s\n", P.name) // получаем значение поля 'name' переменной p
```
Есть еще три способа определить `struct`:
- Присвоить начальные значения по порядку:
```Go
P := person{"Tom", 25}
```
- Использовать формат `поле:значение`, чтобы задать начальные значения полей структуры, при этом можно не соблюдать порядок, в котором поля шли при описании структуры:
```Go
P := person{age: 24, name: "Bob"}
```
- Определить анонимную структуру, а затем задать ей значения:
```Go
P := struct{name string; age int}{"Amy", 18}
```
Давайте рассмотрим конкретный пример:
```Go
package main
import "fmt"
// Определяем новый тип
type person struct {
name string
age int
}
// сравниваем возраст у двух людей, затем возвращаем возраст старшего из них и разницу в возрасте.
// структуры передаются по значению
func Older(p1, p2 person) (person, int) {
if p1.age > p2.age {
return p1, p1.age - p2.age
}
return p2, p2.age - p1.age
}
func main() {
var tom person
// задаем первоначальные значения
tom.name, tom.age = "Tom", 18
// задаем значения в формате "поле:значение"
bob := person{age: 25, name: "Bob"}
// задаем значения в порядке, указанном при определении структуры
paul := person{"Paul", 43}
tb_Older, tb_diff := Older(tom, bob)
tp_Older, tp_diff := Older(tom, paul)
bp_Older, bp_diff := Older(bob, paul)
fmt.Printf("Из %s и %s %s старше на %d лет\n", tom.name, bob.name, tb_Older.name, tb_diff)
fmt.Printf("Из %s и %s %s старше на %d лет\n", tom.name, paul.name, tp_Older.name, tp_diff)
fmt.Printf("Из %s и %s %s старше на %d лет\n", bob.name, paul.name, bp_Older.name, bp_diff)
}
```
### Встраиваемые поля в структуре
Я только что показал Вам, как определять структуру с именами и типами полей. Но Go поддерживает и поля с типами, но без имен. Мы называем это встраиваемыми полями.
Когда встраиваемое поле - структура, все поля этой структуры неявно становятся полями структуры, в которую оно встроено.
Посмотрим на пример:
```Go
package main
import "fmt"
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // встраиваемое поле; это означает, что структура Student включает в себя все поля структуры Human.
specialty string
}
func main() {
// инициализируем студента
mark := Student{Human{"Марк", 25, 120}, "Компьютерные науки"}
// получаем доступ к полям
fmt.Println("Его имя: ", mark.name)
fmt.Println("Его возраст: ", mark.age)
fmt.Println("Его масса: ", mark.weight)
fmt.Println("Его специализация: ", mark.specialty)
// изменяем значения полей
mark.specialty = "Искусственный интеллект"
fmt.Println("Марк поменял специализацию")
fmt.Println("Его специализация: ", mark.specialty)
// изменяем возраст
fmt.Println("Марк постарел")
mark.age = 46
fmt.Println("Его возраст: ", mark.age)
// изменяем массу
fmt.Println("Марк больше не атлет")
mark.weight += 60
fmt.Println("Его масса: ", mark.weight)
}
```
![](images/2.4.student_struct.png?raw=true)
Рисунок 2.7 Наследование в Student и Human
Мы видим, что можно иметь доступ к значениям полей Student так же, как и к Human. Так работают встраиваемые поля. Очень круто, не так ли? Держитесь, есть кое-что покруче! Вы можете использовать Student, чтобы получить доступ к Human в этом встраиваемом поле!
```Go
mark.Human = Human{"Маркус", 55, 220}
mark.Human.age -= 1
```
Все типы данных в Go могут быть использованы в качестве встраиваемых полей:
```Go
package main
import "fmt"
type Skills []string
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // struct как встраиваемое поле
Skills // срез из строк как встраиваемое поле
int // встроенный тип как встраиваемое поле
specialty string
}
func main() {
// Инициализируем студента Джейн
jane := Student{Human:Human{"Джейн", 35, 100}, specialty:"Биология"}
// доступ к полям
fmt.Println("Ее имя: ", jane.name)
fmt.Println("Ее возраст: ", jane.age)
fmt.Println("Ее масса: ", jane.weight)
fmt.Println("Ее специализация: ", jane.specialty)
// изменяем поле навыков
jane.Skills = []string{"анатомия"}
fmt.Println("Ее навыки: ", jane.Skills)
fmt.Println("Она овладела еще двумя навыками: ")
jane.Skills = append(jane.Skills, "физика", "golang")
fmt.Println("Теперь ее навыки: ", jane.Skills)
// изменяем встраиваемое поле
jane.int = 3
fmt.Println("Ее любимое число: ", jane.int)
}
```
В примере выше мы можем видеть, что данные всех типов могут быть встраиваемыми полями, и мы можем использовать функции, чтобы оперировать ими.
Есть, впрочем, одна проблема. Если у `Human` есть поле под названием `phone`, а у `Student` тоже есть поле с таким именем, как нам быть?
В Go есть простой способ решить эту задачу. Внешние поля имеют уровень доступа выше, что означает, что, обращаясь к `student.phone`, мы оперируем с полем `phone` в `student`,а не в `Human`. Это свойство проще представить как `перегрузку` полей.
```Go
package main
import "fmt"
type Human struct {
name string
age int
phone string // у Human есть поле phone
}
type Employee struct {
Human // встраиваемое поле Human
specialty string
phone string // у Employee также появляется поле phone
}
func main() {
Bob := Employee{Human{"Боб", 34, "777-444-XXXX"}, "Дизайнер", "333-222"}
fmt.Println("Рабочий телефон Боба:", Bob.phone)
// оперируем с поле phone в Human
fmt.Println("Личный телефон Боба:", Bob.Human.phone)
}
```
## Ссылки
- [Содержание](preface.md)
- Предыдущий раздел: [Управляющие конструкции и функции](02.3.md)
- Следующий раздел: [Объектно-ориентированное программирование](02.5.md)