sort
пакет:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
В чем смысл анонимного интерфейса Interface
в структуре reverse
?
sort
пакет:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
В чем смысл анонимного интерфейса Interface
в структуре reverse
?
Ответы:
Таким образом, 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
Метод for reverse
вызывает 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
}