Шаг 98.
Язык программирования Go.
Встраивание интерфейсов в структурах (окончание)

    На этом шаге мы завершим рассмотрение примера встраивания интерфейсов в структуры.

    Реализация структуры StringOption похожа на реализацию структуры IntOption, различия заключаются только в поле Value, имеющем тип string, и в методе IsValid(), возвращающем true, если поле Value содержит непустое значение.

type StringOption struct {
	OptionCommon        // Анонимное поле (встраивание)
	Value        string // именованное поле (агрегирование)
}
func (option StringOption) Name() string {
	return name(option.ShortName, option.LongName)
}
func (option StringOption) IsValid() bool {
	return option.Value != "" 
}

    При реализации структуры FloatOption был использован прием встраивания интерфейса, только чтобы показать, как это делается. Это законченная реализация структуры FloatOption. Встроенное поле Optioner означает, что при создании значений типа FloatOption встроенному полю должно присваиваться значение, удовлетворяющее требованиям интерфейса Optioner.

type FloatOption struct {
	Optioner         /* Анонимное поле (встраивание интерфейса:
                            требует конкретный тип)*/
	Value    float64  // Именованное поле (агрегирование)
} 

    Тип FloatOption имеет встроенное поле типа Optioner, поэтому для него требуется указывать значение конкретного типа, реализующего интерфейс Optioner. Это требование можно удовлетворить присваиванием полю Optioner в структуре FloatOption значения типа GenericOption. Ниже представлена законченная реализация структуры GenericOption – типа, удовлетворяющего требованиям интерфейса Optioner.

type GenericOption struct {
	OptionCommon // Анонимное поле (встраивание)
}

func (option GenericOption) Name() string {
	return name(option.ShortName, option.LongName)
}
func (option GenericOption) IsValid() bool {
	return true
}

    Пример создания и использования новых типов. Каждый тип параметров имеет поле Value, но все они имеют разные типы, например поле StringOption.Value – тип string. Поэтому для доступа к конкретным полям Value (и аналогично к любым другим полям или методам, имеющим определенный тип), необходимо преобразовать текущее значение option в значение конкретного типа. Это легко достигается с помощью инструкции switch выбора по типу. В выражении переключателя типа используется затеняющая переменная (option), которая всегда будет иметь правильный тип для выполняемой инструкции case (например, в инструкции case StringOption переменная option будет иметь тип StringOption и т. д.). Поэтому в каждой ветке case можно обращаться к любым полям и методам с учетом их фактических типов.

func main() {
   fileOption := StringOption{OptionCommon{"f", "file"}, "index.html"}
   sizeOption := FloatOption{
		GenericOption{OptionCommon{"s", "size"}}, 19.5}
   for _, option := range []Optioner{fileOption, sizeOption} {
      fmt.Print("name=", option.Name(), " • valid=", option.IsValid())
      fmt.Print(" • value=")
      switch option := option.(type) { // затеняющая переменная
        case StringOption:
	    fmt.Println(option.Value)
	case FloatOption:
	    fmt.Println(option.Value)
	}
   }
}  

    Архив с примером можно взять здесь.

    На следующем шаге рассмотрим пример создания пользовательского типа с единственным значением.


Предыдущий шаг Содержание Следующий шаг