На этом шаге мы рассмотрим встраивание интерфейсов в Go.
Помимо агрегирования и встраивания в структуры конкретных типов, имеется также возможность агрегирования и встраивания интерфейсов. Когда структура включает агрегированное (именованное) или встроенное (анонимное) поле с типом интерфейса, это означает, что структура может хранить в таком поле любое значение, реализующее методы указанного интерфейса.
Рассмотрим пример, демонстрирующий, как можно организовать поддержку параметров командной строки, имеющих короткие и длинные имена (например, "-o" и "--outfile") и принимающих значения определенных типов (int, float64, string), а также некоторых общих методов.
/*Интерфейс Optioner определяет общие методы для всех типов параметров, поддержку которых требуется реализовать*/ type Optioner interface { Name() string IsValid() bool } /*Структура OptionCommon имеет два поля, общих для всех параметров*/ type OptionCommon struct { ShortName string LongName string } /*Реализация типа IntOption и неэкспортируемой функции name(). Благодаря встраиванию структуры OptionCommon появляется возможность обращаться к ее полям непосредственно, как это делается в методе IntOption.Name(). Структура IntOption удовлетворяет требованиям интерфейса Optioner (поскольку предоставляет методы Name() и IsValid() с требуемыми сигнатурами)*/ type IntOption struct { OptionCommon // Анонимное поле (встраивание) Value, Min, Max int // именованное поле (агрегирование) } func (option IntOption) Name() string { return name(option.ShortName, option.LongName) } func (option IntOption ) IsValid() bool { return option.Min <= option.Value && option.Value <= option.Max } /*Функция name() реализована в отдельной функции, что дает возможность использовать эту функцию в других типах параметров*/ func name(shortName, longName string) string { if longName == "" { return shortName } return longName } /*Пример создания и использования значения типа IntOption*/ func main() { //---------------------------------- /*В переменной topOption типа IntOption можно присвоить значение только полям OptionCommon и Max, т.к. для других полей (Value и Min) подходят и нулевые значения. В языке Go допускается создавать структуры и инициализировать только отдельные поля, используя синтаксис имяПоля: значениеПоля. Всем полям, не инициализированным явно, автоматически присваиваются соответствующие нулевые значения*/ topOption := IntOption{ OptionCommon: OptionCommon{"t", "top"}, Value: 45, Min: 10, Max: 100, } //---------------------------------- /*Относительно переменной option можно вызывать любые методы, определяемые интерфейсом Optioner, как это сделано в теле цикла, где вызываются методы Option.Name() и Option.IsValid()*/ for _, option := range []Optioner{topOption} { fmt.Print("name=", option.Name(), " • valid=", option.IsValid()) fmt.Print(" • value=") switch option := option.(type) { // затеняющая переменная case IntOption: fmt.Print(option.Value, " • min=", option.Min, " • max=", option.Max, "\n") //---------------------------------- } } }
На следующем шаге продолжим рассматривать пример встраивания интерфесов в структурах.