В Go используется встраивание (embedding) структур и интерфейсов вместо классического наследования из ООП-языков. Это принципиально другой подход, который часто вызывает вопросы у разработчиков, пришедших из других языков.
Характеристика | Наследование (ООП) | Встраивание (Go) |
---|---|---|
Отношение | "is-a" (является) | "has-a" (содержит) |
Полиморфизм | Иерархия типов | Композиция интерфейсов |
Переопределение | Виртуальные методы | Нет переопределения |
Доступ | protected/private модификаторы | Все поля/методы доступны |
Явность | Неявное поведение родителя | Явное обращение к встроенному полю |
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Sound from " + a.Name
}
type Dog struct {
Animal // Встраивание (не наследование!)
Breed string
}
func main() {
d := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Labrador",
}
fmt.Println(d.Speak()) // Обращение к методу встроенной структуры
fmt.Println(d.Name) // Прямой доступ к полю встроенной структуры
}
func (d Dog) Speak() string {
return "Bark from " + d.Name
}
func main() {
d := Dog{Animal: Animal{Name: "Rex"}}
fmt.Println(d.Speak()) // Bark from Rex
fmt.Println(d.Animal.Speak()) // Sound from Rex
}
func MakeSound(a Animal) {
fmt.Println(a.Speak())
}
func main() {
d := Dog{Animal: Animal{Name: "Rex"}}
// MakeSound(d) // Ошибка компиляции: Dog не является Animal
MakeSound(d.Animal) // Работает, но это другой объект
}
type Cat struct {
Animal
}
func main() {
c := Cat{Animal: Animal{Name: "Whiskers"}}
fmt.Println(c.Animal.Name) // Всегда можно обратиться к исходному полю
}
Избегание хрупких иерархий
Классические проблемы "алмаза смерти" в множественном наследовании невозможны.
Ясность кода
Все зависимости видны явно через композицию.
Гибкость
Можно встраивать несколько структур и интерфейсов:
type Writer interface {
Write([]byte) (int, error)
}
type Logger struct {
Writer // Встраивание интерфейса
Prefix string
}
Пример правильного использования:
type ReadWriter interface {
Reader
Writer
}
type File struct {
*os.File
}
func (f File) Close() error {
// Собственная реализация Close
return f.File.Close()
}
Встраивание в Go — это механизм композиции, а не наследования. Оно обеспечивает повторное использование кода без создания жестких иерархий типов. Это соответствует философии Go, где композиция предпочтительнее классического наследования. Встраивание делает зависимости явными, код более гибким и избегает многих проблем традиционных ООП-систем.