← Назад к вопросам

Как устроена объектная модель в Go?

1.0 Junior🔥 81 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Объектная модель в Go: композиция и интерфейсы вместо классического наследования

Go реализует объектно-ориентированную модель (ООП) через типы, методы и интерфейсы, но без традиционной иерархии классов и наследования. Основные принципы — композиция, инкапсуляция и полиморфизм, достигаемые иначе, чем в языках вроде Java или C++. Рассмотрим ключевые аспекты.

Структуры как основа объектов

В Go роль "классов" выполняют структуры (struct), которые инкапсулируют состояние. Методы привязываются к типам через получатели (receivers).

type User struct {
    ID   int
    Name string
    // Поля приватны, если начинаются со строчной буквы
    email string 
}

// Метод с получателем по значению
func (u User) GetName() string {
    return u.Name
}

// Метод с получателем по указателю для модификации
func (u *User) SetEmail(email string) {
    u.email = email
}

Инкапсуляция контролируется регистром первой буквы: публичные поля/методы — с заглавной, приватные — со строчной. Это касается и пакетов (package-level visibility).

Композиция вместо наследования

Go предпочитает композицию (composition) через встраивание (embedding). Структура может включать другие типы, получая их методы (делегирование).

type Admin struct {
    User          // Встраивание: Admin "имеет" User
    Permissions []string
}

admin := Admin{
    User: User{ID: 1, Name: "Alice"},
}
admin.GetName() // Метод User доступен напрямую

Это делегирование вызовов, а не наследование. Admin не является подтипом User в классическом смысле, но может использовать его поведение. Можно переопределить методы:

func (a Admin) GetName() string {
    return "Admin: " + a.User.GetName()
}

Интерфейсы для полиморфизма

Полиморфизм достигается через интерфейсы (interface) — неявно реализуемые контракты. Тип реализует интерфейс, если содержит все его методы. Это обеспечивает гибкость и абстракцию.

type Speaker interface {
    Speak() string
}

type Dog struct{}
func (d Dog) Speak() string { return "Woof!" }

type Human struct{}
func (h Human) Speak() string { return "Hello!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

MakeSound(Dog{}) // Woof!
MakeSound(Human{}) // Hello!

Интерфейсы в Go:

  • Неявные: не нужно явно указывать implements.
  • Утиная типизация (duck typing) на этапе компиляции.
  • Могут быть пустыми (interface{}, с Go 1.18 — any), что позволяет работать с любыми типами (используется в обобщённом коде).
  • Содержат таплы (type, value) для динамической диспетчеризации.

Методы и получатели

Методы могут иметь получатели по значению или по указателю:

  • По значению: работает с копией, не изменяет оригинал.
  • По указателю: может модифицировать объект; необходимо для структур больших размеров.
func (u User) ValueReceiver() { /* копия */ }
func (u *User) PointerReceiver() { /* оригинал */ }

Компилятор автоматически преобразует вызовы: user.PointerReceiver() работает, даже если user — не указатель.

Отличия от классического ООП

  1. Нет классов — есть типы и структуры.
  2. Нет наследования — используется композиция и встраивание.
  3. Нет перегрузки методов — имена методов в пределах типа должны быть уникальны.
  4. Интерфейсы реализуются неявно — это уменьшает связанность кода.
  5. Поведение привязано к типам, а не к иерархиям.

Пример комплексного использования

// Абстракция через интерфейс
type Storage interface {
    Save(data []byte) error
}

// Реализация
type FileStorage struct {
    path string
}
func (fs *FileStorage) Save(data []byte) error {
    return os.WriteFile(fs.path, data, 0644)
}

// Композиция с встраиванием
type LoggedStorage struct {
    Storage // Встраиваем интерфейс
}
func (ls LoggedStorage) Save(data []byte) error {
    fmt.Println("Saving...")
    return ls.Storage.Save(data) // Делегирование
}

// Использование
var store Storage = &LoggedStorage{Storage: &FileStorage{path: "test.txt"}}
store.Save([]byte("data"))

Объектная модель в Go — это прагматичный подход, где композиция и интерфейсы предпочтительнее сложных иерархий. Она способствует созданию модульного, тестируемого кода с низкой связанностью, что соответствует философии языка — простота и эффективность.