Go: Структуры и ООП

21 вопросов

1 Есть ли в Go ООП?

В классическом виде (классы, наследование) - нет. Есть инкапсуляция (экспорт через регистр), полиморфизм через интерфейсы, методы у типов. Композиция через встраивание структур вместо наследования. ООП в Go - без иерархии классов.

Открыть отдельно →
2 Как реализована инкапсуляция в Go?

Регистр имени: имена с заглавной буквы экспортируются (публичные), с маленькой - только внутри пакета. Поля структуры и методы подчиняются этому правилу. "Приватные" поля и методы доступны только в том же пакете. Сеттеры/геттеры - по соглашению, когда нужна логика доступа.

Открыть отдельно →
3 Как в Go делают полиморфизм?

Через интерфейсы. Тип не объявляет интерфейс, но если у него есть нужные методы - он подходит. Функция принимает интерфейс и работает с любым типом, его реализующим. Подмена реализации (в том числе в тестах) - через разные типы, удовлетворяющие одному интерфейсу.

Открыть отдельно →
4 Сколько памяти занимает пустая структура struct{}?

Ноль байт (или минимальный размер, чтобы адрес был уникальным - зависит от реализации). Несколько переменных типа struct{} могут иметь один и тот же адрес. Используется как маркер или в map/set для множеств без значения.

Открыть отдельно →
5 Где используют пустую структуру?

Как тип значения в map для set: map[T]struct{}. Сигнал в каналах: chan struct{}, done <- struct{}{}. Вместо bool когда важно только наличие. Как тип данных не хранит ничего - экономия памяти.

Открыть отдельно →
6 Что такое pointer receiver у метода?

Метод с получателем-указателем: func (p *T) M(). Вызывается и для T, и для *T (компилятор подставит адрес). В методе можно менять поля получателя. Для больших структур и когда нужно изменять состояние используют pointer receiver.

Открыть отдельно →
7 Когда использовать value receiver, когда pointer receiver?

Value: маленькие структуры, неизменяемые типы, когда не нужна мутация. Pointer: большие структуры (избежание копирования), когда метод меняет получателя или структура содержит мьютекс/слайс/map. Единообразие: если один метод с pointer receiver - обычно все с pointer.

Открыть отдельно →
8 Чем метод отличается от функции в Go?

Метод привязан к типу: func (r T) M(). Вызов: x.M(), в методе r - получатель. Функция не привязана: func F(x T). Методы могут быть только у типов, определённых в том же пакете. Интерфейсы требуют методы, не функции.

Открыть отдельно →
9 Что такое встраивание (embedding) структур?

Включение типа без имени поля: type B struct { A }. Поля и методы A доступны на B напрямую (promoted): b.FieldA. Внешняя структура "включает" встроенную. Конфликт имён - явно указывать вложенный тип. Наследования нет - только композиция.

Открыть отдельно →
10 Чем встраивание отличается от наследования?

Встраивание - композиция: тип B содержит A, но B не "подтип" A. Нет виртуальных методов и переопределения в смысле ООП. Методы A продвигаются на B; при том же имени метод внешней структуры "затеняет". Нет полиморфизма по иерархии - полиморфизм через интерфейсы.

Открыть отдельно →
11 Как в Go соблюдают dependency inversion?

Зависимости определяются через интерфейсы, а не конкретные типы. Пакет верхнего уровня объявляет интерфейс с нужными методами; нижний уровень реализует его. Внедрение зависимостей - передача реализации (структуры) в конструктор или через опции. Тесты подставляют моки того же интерфейса.

Открыть отдельно →
12 Что такое экспортируемые и неэкспортируемые имена?

Имена с заглавной буквы - экспортируемые (видны из других пакетов). С маленькой - неэкспортируемые (только текущий пакет). Применяется к типам, полям структур, функциям, методам, константам, переменным. Определяет публичный API пакета.

Открыть отдельно →
13 Можно ли определить свои методы для встроенных типов?

Нет. Методы можно объявлять только для типов, определённых в том же пакете. Для встроенных (int, string и т.д.) нужно объявить свой тип: type MyInt int, затем func (m MyInt) Foo() {}. MyInt и int - разные типы, приведение через MyInt(x).

Открыть отдельно →
14 Влияет ли порядок полей на размер структуры? Выравнивание?

Да. Поля выравниваются по границам (alignment); между полями может быть padding. Порядок полей от большего к меньшему по выравниванию часто уменьшает размер структуры. Пустые поля struct{} в конце могут не занимать места; в середине - могут для выравнивания.

Открыть отдельно →
15 Как порядок полей влияет на память структуры?

Компилятор выравнивает поля (например, int64 по 8 байт). Неоптимальный порядок добавляет padding. Пример: два bool и int64 - лучше int64, затем bool, чтобы не было лишних байт между ними. unsafe.Sizeof и unsafe.Offsetof показывают размер и смещения.

Открыть отдельно →
16 Что такое затенение (shadowing) методов при встраивании?

Если встроенная и внешняя структура имеют метод с одним именем, вызывается метод внешней. Внешний метод "затеняет" встроенный. Доступ к встроенному явно: b.A.M(). Для переопределения поведения внешняя структура объявляет свой метод с тем же именем.

Открыть отдельно →
17 Как расширить стандартный тип (например, []int)?

Объявить свой тип на основе стандартного: type MySlice []int. Для MySlice можно объявить методы. Нельзя добавить методы напрямую к []int. Приведение: MySlice(s), []int(m). Встроенные методы (len, cap и т.д.) остаются; свои добавляются отдельно.

Открыть отдельно →
18 Что такое promoted methods при встраивании?

Методы встроенного типа "поднимаются" на внешнюю структуру и вызываются как методы внешней. type B struct { A }; func (a A) F() {}; тогда b.F() валиден и вызывает A.F с получателем b.A. Если у B есть свой F, вызывается B.F (затенение).

Открыть отдельно →
19 Сравниваемы ли структуры?

Да, если все поля сравниваемы (comparable). Структуры с полями-слайсами, map или функциями сравнивать нельзя. == сравнивает поля поэлементно. Для сложных структур иногда удобнее сравнивать по отдельным полям или через reflect.DeepEqual (учитывает типы и вложенность).

Открыть отдельно →
20 Что такое анонимная структура?

Структура без имени типа: struct{ X int }{X: 1} или var v struct{ Name string }. Используется для разовых данных, JSON-ответов, тестовых фикстур. Нельзя объявить методы у анонимной структуры - только у именованного типа.

Открыть отдельно →
21 Чем глубокое копирование отличается от поверхностного?

Поверхностное: копируются поля; если поле - указатель или слайс, копируется только указатель/заголовок, данные общие. Глубокое: рекурсивно копировать всё, включая данные по указателям и элементы слайсов. В Go копирование структуры по умолчанию поверхностное. Глубокое - вручную или через библиотеки/рефлексию.

Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.