Коротко: в Go нет классов и наследования: данные — структуры, поведение — методы, контракты — интерфейсы (реализуются неявно); композиция вместо иерархий.

Коротко: в Go нет классов и наследования. Данные описываются структурами, поведение — методами, а контракты — интерфейсами, причём интерфейсы реализуются неявно. Для PHP-разработчика это смена парадигмы: вместо иерархий наследования — композиция и маленькие интерфейсы. Разберём на параллелях с PHP.

Структура вместо класса-данных

Там, где в PHP вы бы завели класс со свойствами, в Go используется структура:

type User struct {
    Name  string
    Email string
    Age   int
}

u := User{Name: "Лео", Email: "leo@example.com", Age: 36}

Это чистый контейнер данных. Никакого private/public на уровне ключевых слов — видимость определяется регистром первой буквы: Name (с большой) экспортируется из пакета, name (с маленькой) — приватно для пакета.

Методы вместо методов класса

Метод в Go — это функция с «получателем» (receiver). Он привязывается к типу, но объявляется отдельно от структуры:

func (u User) FullInfo() string {
    return u.Name + " <" + u.Email + ">"
}

// указательный получатель — если нужно менять состояние
func (u *User) Birthday() {
    u.Age++
}

Различие (u User) и (u *User) важно: первый работает с копией, второй — с указателем и может менять оригинал. В PHP объекты всегда передаются по ссылке-хэндлу, в Go вы выбираете явно.

Интерфейсы — и тут главное отличие

Интерфейс в Go описывает набор методов. Но реализуется он неявно: типу не нужно писать implements — достаточно иметь нужные методы:

type Greeter interface {
    Greeting() string
}

type User struct{ Name string }

func (u User) Greeting() string { return "Привет, " + u.Name }

// User автоматически удовлетворяет Greeter — никаких implements
var g Greeter = User{Name: "Лео"}

Сравните с PHP, где нужно явно class User implements Greeter. В Go связь «утиная»: «если крякает как утка — значит утка». Это развязывает код: интерфейс можно объявить там, где он используется, не трогая тип, который его реализует.

Маленькие интерфейсы вместо больших

Идиома Go — интерфейсы из одного-двух методов. Классика — io.Writer:

type Writer interface {
    Write(p []byte) (n int, err error)
}

Функции принимают узкий интерфейс, а не конкретный тип, — и работают с чем угодно, что его реализует (файл, сеть, буфер). В PHP к этому приходят через интерфейс-сегрегацию (буква I в SOLID); в Go это встроенная культура.

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

Наследования классов в Go нет. Переиспользование — через встраивание (embedding) структур и интерфейсов:

type Timestamps struct {
    CreatedAt time.Time
    UpdatedAt time.Time
}

type Article struct {
    Timestamps // встроено: поля и методы Timestamps доступны напрямую
    Title string
}

a := Article{}
a.CreatedAt = time.Now() // доступ как к собственному полю

Это «has-a», а не «is-a». Никаких хрупких глубоких иерархий — то, к чему в PHP приходят осознанно («композиция вместо наследования»), в Go является способом по умолчанию.

Полиморфизм через интерфейсы

Полиморфизм в Go достигается интерфейсами + (при необходимости) type switch:

func describe(i interface{}) string {
    switch v := i.(type) {
    case User:
        return "пользователь " + v.Name
    case int:
        return "число " + strconv.Itoa(v)
    default:
        return "что-то ещё"
    }
}

Шпаргалка соответствий PHP → Go

  • класс с данными → struct;
  • метод класса → функция с receiver;
  • implements Interface → ничего (неявная реализация);
  • наследование (extends) → встраивание (embedding), композиция;
  • abstract/контракт → интерфейс (маленький);
  • видимость public/private → регистр первой буквы имени.

Главный сдвиг мышления: в Go вы проектируете вокруг поведения (интерфейсов) и композиции, а не вокруг иерархий классов. Если переносите PHP-домен на Go и не уверены, как разложить модель на структуры и интерфейсы, — разберём на консультации.