Урок раскрывает концепцию встраивания типов в Go: объединение интерфейсов, продвижение полей и методов структур, переопределение поведения и отличие embedding от наследования. Даётся полный разбор всех ключевых примеров и моделей композиции.
Встраивание типов --- это фундаментальный механизм композиции в Go, позволяющий расширять поведение структур и интерфейсов без наследования. Благодаря нему можно объединять интерфейсы, продвигать методы и поля встроенных структур, а также переопределять их поведение, создавая чистые и выразительные API.
Интерфейсы в Go можно объединять: один интерфейс может включать другой. Это позволяет строить большие интерфейсы из маленьких и логически связанных.
type Whisperer interface {
Whisper() string
}
type Yeller interface {
Yell() string
}
type Talker interface {
Whisperer
Yeller
}
Интерфейс Talker требует реализации методов и Whisperer, и Yeller.
Любой тип становится Talker, если у него есть оба метода -
неявно, без ключевого слова implements.
func talk(t Talker) {
fmt.Println(t.Yell())
fmt.Println(t.Whisper())
}
Такой подход делает код гибким и использует чистую композицию поведения.
Встраивание структур позволяет включить одну структуру внутрь другой. При этом:
type Account struct {
ID int
Balance int
Name string
}
func (a *Account) GetBalance() int {
return a.Balance
}
func (a Account) String() string {
return fmt.Sprintf("Standard (%d) $%d "%s"", a.ID, a.Balance, a.Name)
}
type ManagerAccount struct {
Account
}
Теперь ManagerAccount автоматически имеет:
ID, Balance, Name;GetBalance(), String().mgr := ManagerAccount{Account{2, 30, "Cassandra"}}
fmt.Println(mgr.ID) // поле встроенной структуры
fmt.Println(mgr.GetBalance()) // метод встроенной структуры
fmt.Println(mgr.String()) // вызов метода String() от Account
Это и есть продвижение полей и методов.
Если во встраивающей структуре объявить метод с таким же именем - он заменит метод встроенной структуры.
func (m ManagerAccount) String() string {
return fmt.Sprintf("Manager (%d) $%d "%s"",
m.ID, m.Balance, m.Name)
}
Теперь:
mgr.String() // вызывает версию ManagerAccount, а не Account
Переопределение позволяет адаптировать поведение встроенного типа.
Наследование Встраивание
Родитель → потомок Композиция: тип включает другой тип Жёсткая иерархия Гибкая структура Связь «is-a» Связь «has-a» Сложные цепочки наследования Простая и плоская композиция Потенциальные проблемы переопределения Чёткая модель поведения
Композиция через embedding --- предпочтительный подход в Go.