(Статья 5 из цикла "Golang с нуля")
Мы уже изучили базовые элементы Go, а также его мощные инструменты для параллелизма. Теперь пришло время разобраться, как создавать собственные типы данных и работать с ними. Здесь на помощь приходят структуры и интерфейсы, которые являются ключевыми элементами для организации кода в Go.
🔹 Структуры (structs): как создавать свои типы
Структура — это коллекция полей разного типа, объединённых в один тип. Она похожа на классы в других языках, но без наследования и методов, определённых внутри. Структуры позволяют логически группировать связанные данные.
Пример создания структуры:
package main
import "fmt"
// Создаем структуру User
type User struct {
Name string
Age int
IsAdmin bool
}
func main() {
// Создаем переменную типа User
user1 := User{
Name: "Алиса",
Age: 30,
IsAdmin: true,
}
// Обращение к полям структуры
fmt.Println("Имя:", user1.Name)
fmt.Println("Возраст:", user1.Age)
// Изменение поля
user1.Age = 31
fmt.Println("Новый возраст:", user1.Age)
}
🔹 Методы: функции для структур
К структурам можно привязывать функции, которые в Go называются методами. Метод — это функция, объявленная с так называемым ресивером (receiver), который связывает её со структурой.
Пример метода:
package main
import "fmt"
type User struct {
Name string
Age int
}
// Метод, который выводит информацию о пользователе
func (u User) SayHello() {
fmt.Printf("Привет, меня зовут %s, мне %d лет.\n", u.Name, u.Age)
}
func main() {
user1 := User{Name: "Боб", Age: 25}
user1.SayHello() // Вызов метода
}
🔹 Интерфейсы: поведение, а не реализация
Интерфейс — это набор сигнатур методов без их реализации. Он определяет поведение, которым должен обладать тип, чтобы соответствовать этому интерфейсу.
Интерфейсы — это основа полиморфизма в Go. Тип автоматически "реализует" интерфейс, если у него есть все методы, описанные в интерфейсе. Явного объявления о реализации не требуется.
Пример интерфейса:
package main
import "fmt"
// Определяем интерфейс Shape
type Shape interface {
Area() float64
}
// Структура для круга
type Circle struct {
Radius float64
}
// Структура для квадрата
type Square struct {
Side float64
}
// Метод Area для Circle
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
// Метод Area для Square
func (s Square) Area() float64 {
return s.Side * s.Side
}
func main() {
circle := Circle{Radius: 5}
square := Square{Side: 4}
// Функция, которая принимает любой тип, реализующий интерфейс Shape
printArea(circle)
printArea(square)
}
func printArea(s Shape) {
fmt.Printf("Площадь: %.2f\n", s.Area())
}
В этом примере и Circle, и Square реализуют интерфейс Shape (потому что у них обоих есть метод Area()), и их можно передавать в функцию printArea.
💡 Вывод
Структуры и методы позволяют создавать сложные и организованные типы данных, а интерфейсы дают возможность писать гибкий и полиморфный код. Эта комбинация — мощный инструмент для построения масштабируемых и легко поддерживаемых приложений.
Что дальше?
В следующей статье мы рассмотрим пакеты и модули, чтобы понять, как организовать свой код в большие проекты и управлять зависимостями.
Попробуйте создать собственную структуру с несколькими методами и интерфейсом, который она реализует! Поделитесь своими идеями в комментариях!