суббота, 26 января 2019 г.

Эффективный Go: интерфейсы, преобразования

Интерфейсы в Go предоставляют способ указать поведение объекта: если что-то может сделать это, тогда это можно использовать здесь. В предыдущих постах мы уже рассматривали пару простых примеров; пользовательские принтеры могут быть реализованы методом String, в то время как Fprintf может генерировать вывод для чего угодно с помощью метода Write. Интерфейсы только с одним или двумя методами распространены в коде Go и обычно им дается имя, полученное из метода, например io.Writer для чего-то, который реализует Write.

Тип может реализовывать несколько интерфейсов. Например, коллекция может быть отсортирована с помощью процедур в пакете sort, если он реализует sort.Interface, который содержит Len(), Less(i, j int) bool и Swap(i, j int), и он также может иметь собственный кастомный форматтер. В этом надуманном примере Sequence удовлетворяет обоим условиям.

type Sequence []int

// Методы требуемые sort.Interface.
func (s Sequence) Len() int {
    return len(s)
}
func (s Sequence) Less(i, j int) bool {
    return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

// Метод для печати - сортирует элементы перед печатью.
func (s Sequence) String() string {
    sort.Sort(s)
    str := "["
    for i, elem := range s {
        if i > 0 {
            str += " "
        }
        str += fmt.Sprint(elem)
    }
    return str + "]"
}

Преобразования

Метод String в Sequence воссоздает работу, которую Sprint уже выполняет для срезов. Мы можем поделить усилия, если мы преобразуем Sequence в обычный []int перед вызовом Sprint.

func (s Sequence) String() string {
    sort.Sort(s)
    return fmt.Sprint([]int(s))
}

Этот метод является еще одним примером техники преобразования для вызова Sprintf безопасно из метода String. Поскольку два типа (Sequence и []int) одинаковы, если мы игнорируем имя типа, это допустимо проводить преобразование между ними. Преобразование не создает новое значение, оно просто временно действует как будто существующее значение имеет новый тип. (Существуют и другие допустимые преобразования, например, из целого числа в число с плавающей запятой, чтобы создать новое значение.)

Это идиома в Go программах - преобразовывать тип выражения для доступа к другому набору методов. В качестве примера мы могли бы использовать существующий тип sort.IntSlice, чтобы уменьшить весь пример:

type Sequence []int

// Метод для печати - сортирует элементы перед печатью.
func (s Sequence) String() string {
    sort.IntSlice(s).Sort()
    return fmt.Sprint([]int(s))
}

Теперь вместо Sequence реализуем несколько интерфейсов (сортировка и печать), мы используем возможность элемента данных быть преобразованными в несколько типов (Sequence, sort.IntSlice и []int), каждый из которых выполняет определенную часть работы. Это более необычно на практике, но может быть эффективным.


Читайте также:


Комментариев нет:

Отправить комментарий