AIUI, эта статья («Внимательный взгляд…») на самом деле не говорит о том, что значит встраивать анонимные интерфейсы в структуру, она просто говорит об интерфейсах в целом.
Адриан Людвин
Ответы:
67
Таким образом, reverse реализует, sort.Interfaceи мы можем переопределить конкретный метод без необходимости определять все остальные.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of// another Interface implementation.
Interface
}
Обратите внимание , как здесь она меняет местами (j,i)вместо (i,j)а также это единственный метод , объявленный для структуры , reverseдаже если reverseреализоватьsort.Interface
// Less returns the opposite of the embedded implementation's Less method.func(r reverse)Less(i, j int)bool {
return r.Interface.Less(j, i)
}
Какую бы структуру ни передали внутри этого метода, мы преобразуем ее в новую reverseструктуру.
// Reverse returns the reverse order for data.funcReverse(data Interface)Interface {
return &reverse{data}
}
Настоящая ценность приходит, если вы думаете, что бы вы сделали, если бы такой подход был невозможен.
Добавить еще один Reverseметод в sort.Interface?
Создать еще один ReverseInterface?
...?
Любое из этих изменений потребует намного большего количества строк кода в тысячах пакетов, которые хотят использовать стандартные функции обратного преобразования.
Значит, это позволяет вам переопределить только некоторые методы интерфейса?
Дэвид 天宇 Вонг
1
Важная часть - reverseэто член типа Interface. Затем у этого члена есть методы, вызываемые во внешней структуре или переопределяемые.
Брайан
Может ли эта функция (или подход) рассматриваться как способ достижения того, что мы делаем в Java через. extendдля расширения не абстрактных подклассов? Для меня это может быть удобным способом переопределить только определенные методы при использовании существующих, которые реализованы внутренним Interface.
Кевин Габуси
Так это что-то вроде наследования? И return r.Interface.Less(j, i)вызывает ли родительскую реализацию?
warvariuc
39
Хорошо, принятый ответ помог мне понять, но я решил опубликовать объяснение, которое, на мой взгляд, лучше соответствует моему образу мышления.
В «Effective Go» есть примеры интерфейсов, в которые встроены другие интерфейсы:
// ReadWriter is the interface that combines the Reader and Writer interfaces.type ReadWriter interface {
Reader
Writer
}
и структура со встроенными другими структурами:
// ReadWriter stores pointers to a Reader and a Writer.// It implements io.ReadWriter.type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Но нет упоминания о структуре со встроенным интерфейсом. Я был смущен, увидев это в sortпакете:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Но идея проста. Это почти то же самое, что:
type reverse struct {
IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}
методы IntSliceпродвижения reverse.
И это:
type reverse struct {
Interface
}
означает, что sort.reverseможно встроить любую структуру, реализующую интерфейс, sort.Interfaceи какие бы методы у этого интерфейса ни были, они будут продвигаться reverse.
sort.Interfaceимеет метод, Less(i, j int) boolкоторый теперь можно переопределить:
// Less returns the opposite of the embedded implementation's Less method.func(r reverse)Less(i, j int)bool {
return r.Interface.Less(j, i)
}
Мое замешательство в понимании
type reverse struct {
Interface
}
Я думал, что структура всегда имеет фиксированную структуру, т.е. фиксированное количество полей фиксированных типов.
Но следующее доказывает мою неправоту:
package main
import"fmt"// some interfacetype Stringer interface {
String() string
}
// a struct that implements Stringer interfacetype Struct1 struct {
field1 string
}
func(s Struct1)String()string {
return s.field1
}
// another struct that implements Stringer interface, but has a different set of fieldstype Struct2 struct {
field1 []string
dummy bool
}
func(s Struct2)String()string {
return fmt.Sprintf("%v, %v", s.field1, s.dummy)
}
// container that can embedd any struct which implements Stringer interfacetype StringerContainer struct {
Stringer
}
funcmain() {
// the following prints: This is Struct1
fmt.Println(StringerContainer{Struct1{"This is Struct1"}})
// the following prints: [This is Struct1], true
fmt.Println(StringerContainer{Struct2{[]string{"This", "is", "Struct1"}, true}})
// the following does not compile:// cannot use "This is a type that does not implement Stringer" (type string)// as type Stringer in field value:// string does not implement Stringer (missing String method)
fmt.Println(StringerContainer{"This is a type that does not implement Stringer"})
}
Если я правильно понимаю, значения интерфейса представлены указателем на присвоенный ему экземпляр и указателем на таблицу методов типа экземпляра. Таким образом, все значения интерфейса имеют одинаковую структуру в памяти. Конструктивно встраивание такое же, как и композиция. Таким образом, даже структура, встраивающая интерфейс, будет иметь фиксированную структуру. Структура экземпляров, на которые указывает интерфейс, будет отличаться.
Nishant George Agrwal
Я нашел этот ответ лучше, чем принятый, поскольку он дал гораздо больше подробностей, ясный пример и ссылку на документацию.
110100100
25
Заявление
type reverse struct {
Interface
}
позволяет инициализировать reverseвсе, что реализует интерфейс Interface. Пример:
&reverse{sort.Intslice([]int{1,2,3})}
Таким образом, все методы, реализованные встроенным Interfaceзначением, заполняются извне, в то время как вы все еще можете переопределить некоторые из них reverse, например, Lessчтобы отменить сортировку.
Я тоже дам свое объяснение. sortПакет определяет неэкспортируемый тип reverse, который является структурой, которая встраивает Interface.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of// another Interface implementation.
Interface
}
Это позволяет Reverse использовать методы другой реализации интерфейса. Это так называемая compositionмощная особенность Go.
LessМетод reverseвызовов Lessметода вложенной Interfaceстоимости, но с индексами переворачивается, поменяв порядок результатов сортировки.
// Less returns the opposite of the embedded implementation's Less method.func(r reverse)Less(i, j int)bool {
return r.Interface.Less(j, i)
}
Lenи Swapдва других метода reverseнеявно предоставляются исходным Interfaceзначением, потому что это встроенное поле. Экспортированная Reverseфункция возвращает экземпляр reverseтипа, который содержит исходное Interfaceзначение.
// Reverse returns the reverse order for data.funcReverse(data Interface)Interface {
return &reverse{data}
}
Для меня это похоже на наследование. « LessМетод for reverseвызывает Lessметод встроенного Interfaceзначения, но с перевернутыми индексами, изменяя порядок результатов сортировки на обратный». - это похоже на вызов родительской реализации.
warvariuc
Пока тип reverse имеет только одно поле, реализующее интерфейс интерфейса, он также становится членом интерфейса интерфейса: 0
Аллан Гуватудде,
1
Я нахожу эту функцию очень полезной при написании моков в тестах .
Вот такой пример:
package main_test
import (
"fmt""testing"
)
// Item represents the entity retrieved from the store// It's not relevant in this exampletype Item struct {
First, Last string
}
// Store abstracts the DB storetype Store interface {
Create(string, string) (*Item, error)
GetByID(string) (*Item, error)
Update(*Item) error
HealthCheck() error
Close() error
}
// this is a mock implementing Store interfacetype storeMock struct {
Store
// healthy is false by default
healthy bool
}
// HealthCheck is mocked functionfunc(s *storeMock)HealthCheck()error {
if !s.healthy {
return fmt.Errorf("mock error")
}
returnnil
}
// IsHealthy is the tested functionfuncIsHealthy(s Store)bool {
return s.HealthCheck() == nil
}
funcTestIsHealthy(t *testing.T) {
mock := &storeMock{}
if IsHealthy(mock) {
t.Errorf("IsHealthy should return false")
}
mock = &storeMock{healthy: true}
if !IsHealthy(mock) {
t.Errorf("IsHealthy should return true")
}
}
Используя:
type storeMock struct {
Store
...
}
Не нужно высмеивать все Storeметоды. Только HealthCheckиздеваться можно, так как в TestIsHealthyтесте используется только этот метод .
Ниже результат testкоманды:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
Реальный пример этого примера использования можно найти при тестировании AWS SDK .
Чтобы сделать это еще более очевидным, вот уродливая альтернатива - минимум, который необходимо реализовать для удовлетворения Storeинтерфейса:
Ответы:
Таким образом, reverse реализует,
sort.Interface
и мы можем переопределить конкретный метод без необходимости определять все остальные.type reverse struct { // This embedded Interface permits Reverse to use the methods of // another Interface implementation. Interface }
Обратите внимание , как здесь она меняет местами
(j,i)
вместо(i,j)
а также это единственный метод , объявленный для структуры ,reverse
даже еслиreverse
реализоватьsort.Interface
// Less returns the opposite of the embedded implementation's Less method. func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
Какую бы структуру ни передали внутри этого метода, мы преобразуем ее в новую
reverse
структуру.// Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
Настоящая ценность приходит, если вы думаете, что бы вы сделали, если бы такой подход был невозможен.
Reverse
метод вsort.Interface
?Любое из этих изменений потребует намного большего количества строк кода в тысячах пакетов, которые хотят использовать стандартные функции обратного преобразования.
источник
reverse
это член типаInterface
. Затем у этого члена есть методы, вызываемые во внешней структуре или переопределяемые.extend
для расширения не абстрактных подклассов? Для меня это может быть удобным способом переопределить только определенные методы при использовании существующих, которые реализованы внутреннимInterface
.return r.Interface.Less(j, i)
вызывает ли родительскую реализацию?Хорошо, принятый ответ помог мне понять, но я решил опубликовать объяснение, которое, на мой взгляд, лучше соответствует моему образу мышления.
В «Effective Go» есть примеры интерфейсов, в которые встроены другие интерфейсы:
// ReadWriter is the interface that combines the Reader and Writer interfaces. type ReadWriter interface { Reader Writer }
и структура со встроенными другими структурами:
// ReadWriter stores pointers to a Reader and a Writer. // It implements io.ReadWriter. type ReadWriter struct { *Reader // *bufio.Reader *Writer // *bufio.Writer }
Но нет упоминания о структуре со встроенным интерфейсом. Я был смущен, увидев это в
sort
пакете:type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) } ... type reverse struct { Interface }
Но идея проста. Это почти то же самое, что:
type reverse struct { IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order }
методы
IntSlice
продвиженияreverse
.И это:
type reverse struct { Interface }
означает, что
sort.reverse
можно встроить любую структуру, реализующую интерфейс,sort.Interface
и какие бы методы у этого интерфейса ни были, они будут продвигатьсяreverse
.sort.Interface
имеет метод,Less(i, j int) bool
который теперь можно переопределить:// Less returns the opposite of the embedded implementation's Less method. func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
Мое замешательство в понимании
type reverse struct { Interface }
Я думал, что структура всегда имеет фиксированную структуру, т.е. фиксированное количество полей фиксированных типов.
Но следующее доказывает мою неправоту:
package main import "fmt" // some interface type Stringer interface { String() string } // a struct that implements Stringer interface type Struct1 struct { field1 string } func (s Struct1) String() string { return s.field1 } // another struct that implements Stringer interface, but has a different set of fields type Struct2 struct { field1 []string dummy bool } func (s Struct2) String() string { return fmt.Sprintf("%v, %v", s.field1, s.dummy) } // container that can embedd any struct which implements Stringer interface type StringerContainer struct { Stringer } func main() { // the following prints: This is Struct1 fmt.Println(StringerContainer{Struct1{"This is Struct1"}}) // the following prints: [This is Struct1], true fmt.Println(StringerContainer{Struct2{[]string{"This", "is", "Struct1"}, true}}) // the following does not compile: // cannot use "This is a type that does not implement Stringer" (type string) // as type Stringer in field value: // string does not implement Stringer (missing String method) fmt.Println(StringerContainer{"This is a type that does not implement Stringer"}) }
источник
Заявление
type reverse struct { Interface }
позволяет инициализировать
reverse
все, что реализует интерфейсInterface
. Пример:&reverse{sort.Intslice([]int{1,2,3})}
Таким образом, все методы, реализованные встроенным
Interface
значением, заполняются извне, в то время как вы все еще можете переопределить некоторые из нихreverse
, например,Less
чтобы отменить сортировку.Вот что на самом деле происходит, когда вы используете
sort.Reverse
. Вы можете прочитать о встраивании в структурном разделе спецификации .источник
Я тоже дам свое объяснение.
sort
Пакет определяет неэкспортируемый типreverse
, который является структурой, которая встраиваетInterface
.type reverse struct { // This embedded Interface permits Reverse to use the methods of // another Interface implementation. Interface }
Это позволяет Reverse использовать методы другой реализации интерфейса. Это так называемая
composition
мощная особенность Go.Less
Методreverse
вызововLess
метода вложеннойInterface
стоимости, но с индексами переворачивается, поменяв порядок результатов сортировки.// Less returns the opposite of the embedded implementation's Less method. func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
Len
иSwap
два других методаreverse
неявно предоставляются исходнымInterface
значением, потому что это встроенное поле. ЭкспортированнаяReverse
функция возвращает экземплярreverse
типа, который содержит исходноеInterface
значение.// Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
источник
Less
Метод forreverse
вызываетLess
метод встроенногоInterface
значения, но с перевернутыми индексами, изменяя порядок результатов сортировки на обратный». - это похоже на вызов родительской реализации.Я нахожу эту функцию очень полезной при написании моков в тестах .
Вот такой пример:
package main_test import ( "fmt" "testing" ) // Item represents the entity retrieved from the store // It's not relevant in this example type Item struct { First, Last string } // Store abstracts the DB store type Store interface { Create(string, string) (*Item, error) GetByID(string) (*Item, error) Update(*Item) error HealthCheck() error Close() error } // this is a mock implementing Store interface type storeMock struct { Store // healthy is false by default healthy bool } // HealthCheck is mocked function func (s *storeMock) HealthCheck() error { if !s.healthy { return fmt.Errorf("mock error") } return nil } // IsHealthy is the tested function func IsHealthy(s Store) bool { return s.HealthCheck() == nil } func TestIsHealthy(t *testing.T) { mock := &storeMock{} if IsHealthy(mock) { t.Errorf("IsHealthy should return false") } mock = &storeMock{healthy: true} if !IsHealthy(mock) { t.Errorf("IsHealthy should return true") } }
Используя:
type storeMock struct { Store ... }
Не нужно высмеивать все
Store
методы. ТолькоHealthCheck
издеваться можно, так как вTestIsHealthy
тесте используется только этот метод .Ниже результат
test
команды:$ go test -run '^TestIsHealthy$' ./main_test.go ok command-line-arguments 0.003s
Реальный пример этого примера использования можно найти при тестировании AWS SDK .
Чтобы сделать это еще более очевидным, вот уродливая альтернатива - минимум, который необходимо реализовать для удовлетворения
Store
интерфейса:type storeMock struct { healthy bool } func (s *storeMock) Create(a, b string) (i *Item, err error) { return } func (s *storeMock) GetByID(a string) (i *Item, err error) { return } func (s *storeMock) Update(i *Item) (err error) { return } // HealthCheck is mocked function func (s *storeMock) HealthCheck() error { if !s.healthy { return fmt.Errorf("mock error") } return nil } func (s *storeMock) Close() (err error) { return }
источник