Ответ
В Go нет встроенной поддержки тип-сумм (также известных как алгебраические типы данных), но этот паттерн можно эффективно эмулировать с помощью интерфейсов и неэкспортируемого метода-маркера.
Тип-сумма — это тип данных, который может принимать значение одного из нескольких определенных, заранее известных типов.
Пример эмуляции в Go
Представим, что у нас есть фигура, которая может быть либо кругом, либо прямоугольником. Мы можем смоделировать это так:
package shapes
import "math"
// Shape — это наш тип-сумма. Только типы из этого пакета могут его реализовать.
type Shape interface {
// isShape — неэкспортируемый метод-маркер.
// Он не дает типам из других пакетов реализовать этот интерфейс.
isShape()
}
type Circle struct {
Radius float64
}
func (c Circle) isShape() {}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) isShape() {}
// Area безопасно работает с любым типом, реализующим Shape
func Area(s Shape) float64 {
switch v := s.(type) {
case Circle:
return math.Pi * v.Radius * v.Radius
case Rectangle:
return v.Width * v.Height
default:
// Этот случай теоретически недостижим, если мы контролируем пакет
panic("неизвестный тип фигуры")
}
}
Как это работает:
- Закрытый интерфейс: Мы создаем интерфейс
Shape
с неэкспортируемым методом (isShape
). Поскольку метод начинается с маленькой буквы, его могут реализовать только типы, объявленные в том же пакете (shapes
). Это эффективно "закрывает" набор возможных типов. - Реализации: Каждый конкретный тип (
Circle
,Rectangle
) реализует этот интерфейс, предоставляя пустую реализацию метода-маркера. - Type Switch: При обработке значения типа
Shape
используетсяtype switch
для безопасного определения конкретного типа и выполнения соответствующей логики. Это гарантирует, что мы обработаем все возможные варианты, определенные в нашем пакете.