sort pacote:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Qual é o significado de interface anônima Interfaceem struct reverse?
sort pacote:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Qual é o significado de interface anônima Interfaceem struct reverse?
Respostas:
Desta forma, o reverso implementa o sort.Interfacee podemos substituir um método específico sem ter que definir todos os outros
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Observe como aqui ele troca em (j,i)vez de (i,j)e também este é o único método declarado para a estrutura, reversemesmo se reverseimplementarsort.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)
}
Qualquer estrutura passada dentro deste método, nós a convertemos em uma nova reverseestrutura.
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
O valor real surge se você pensar no que teria de fazer se essa abordagem não fosse possível.
Reversemétodo ao sort.Interface?Qualquer uma dessas mudanças exigiria muito mais linhas de código em milhares de pacotes que desejam usar a funcionalidade reversa padrão.
reversetenha um membro do tipo Interface. Este membro então tem seus métodos chamáveis na estrutura externa ou substituíveis.
extendpara estender subclasses não abstratas? Para mim, essa pode ser uma maneira útil de substituir apenas certos métodos ao usar os existentes que são implementados por interno Interface.
return r.Interface.Less(j, i)está chamando a implementação pai?
Ok, a resposta aceita me ajudou a entender, mas resolvi postar uma explicação que acho que se adequa melhor ao meu pensamento.
O "Effective Go" tem exemplos de interfaces com outras interfaces incorporadas:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
e uma estrutura tendo outras estruturas incorporadas:
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Mas não há menção de um struct ter embutido uma interface. Fiquei confuso ao ver isso no sortpacote:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Mas a ideia é simples. É quase o mesmo que:
type reverse struct {
IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}
métodos de IntSlicepromoção reverse.
E isto:
type reverse struct {
Interface
}
significa que sort.reversepode incorporar qualquer estrutura que implemente interface sort.Interfacee quaisquer métodos que a interface possua, eles serão promovidos reverse.
sort.Interfacetem método Less(i, j int) boolque agora pode ser substituído:
// 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)
}
Minha confusão no entendimento
type reverse struct {
Interface
}
foi que pensei que uma estrutura sempre tem estrutura fixa, ou seja, número fixo de campos de tipos fixos.
Mas o seguinte prova que estou errado:
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"})
}
A declaração
type reverse struct {
Interface
}
permite que você inicialize reversecom tudo o que implementa a interface Interface. Exemplo:
&reverse{sort.Intslice([]int{1,2,3})}
Dessa forma, todos os métodos implementados pelo Interfacevalor incorporado são populados externamente enquanto você ainda é capaz de substituir alguns deles reverse, por exemplo, Lesspara reverter a classificação.
Isso é o que realmente acontece quando você usa sort.Reverse. Você pode ler sobre a incorporação na seção de estrutura da especificação .
Eu darei minha explicação também. O sortpacote define um tipo não exportado reverse, que é uma estrutura que incorpora Interface.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Isso permite que o Reverse use os métodos de outra implementação de interface. Este é o assim chamado composition, que é um recurso poderoso do Go.
O Lessmétodo para reversechama o Lessmétodo do Interfacevalor incorporado , mas com os índices invertidos, invertendo a ordem dos resultados da classificação.
// 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)
}
Lene Swapos outros dois métodos de reverse, são fornecidos implicitamente pelo Interfacevalor original porque é um campo incorporado. A Reversefunção exportada retorna uma instância do reversetipo que contém o Interfacevalor original .
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Lessmétodo para reversechama o Lessmétodo do Interfacevalor incorporado , mas com os índices invertidos, invertendo a ordem dos resultados da classificação." - isso se parece com a chamada da implementação pai.
Acho esse recurso muito útil ao escrever simulações em testes .
Aqui está um exemplo:
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")
}
}
Usando:
type storeMock struct {
Store
...
}
Não é necessário zombar de todos os Storemétodos. Só HealthCheckpode ser simulado, já que apenas esse método é usado no TestIsHealthyteste.
Abaixo o resultado do testcomando:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
Um exemplo do mundo real desse caso de uso pode ser encontrado ao testar o SDK da AWS .
Para tornar ainda mais óbvio, aqui está a alternativa desagradável - o mínimo que se precisa implementar para satisfazer a Storeinterface:
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
}