AIUI, aquele artigo ("A Closer Look ...") não fala realmente sobre o que significa embutir interfaces anônimas em uma estrutura, ele apenas fala sobre interfaces em geral.
Adrian Ludwin
Respostas:
67
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.funcReverse(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.
Adicione outro Reversemétodo ao sort.Interface?
Criar outra ReverseInterface?
...?
Qualquer uma dessas mudanças exigiria muito mais linhas de código em milhares de pacotes que desejam usar a funcionalidade reversa padrão.
então permite redefinir apenas alguns dos métodos de uma interface?
David 天宇 Wong
1
O importante é que reversetenha um membro do tipo Interface. Este membro então tem seus métodos chamáveis na estrutura externa ou substituíveis.
Bryan
Poderia este recurso (ou abordagem) ser considerado uma forma de conseguir o que fazemos em Java através. 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.
Kevin Ghaboosi
Então é uma espécie de herança? E return r.Interface.Less(j, i)está chamando a implementação pai?
warvariuc de
39
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 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"})
}
Se meu entendimento estiver correto, os valores da interface são representados por um ponteiro para a instância atribuída a ela e um ponteiro para a tabela de métodos do tipo da instância. Portanto, todos os valores de interface têm a mesma estrutura na memória. Estruturalmente, a incorporação é o mesmo que a composição. Portanto, mesmo uma estrutura incorporando uma interface teria uma estrutura fixa. As estruturas das instâncias para as quais a interface aponta serão diferentes.
Nishant George Agrwal
Achei esta uma resposta melhor do que a aceita, pois deu muito mais detalhes, um exemplo claro e um link para a documentação.
110100100
25
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.
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.funcReverse(data Interface)Interface {
return &reverse{data}
}
Para mim, isso parece herança. "O 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.
warvariuc de
Enquanto type reverse tiver apenas um campo que implementa a interface Interface, ele também se tornará um membro da interface: 0
Allan Guwatudde
1
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 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")
}
}
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:
Respostas:
Desta forma, o reverso implementa o
sort.Interface
e podemos substituir um método específico sem ter que definir todos os outrostype 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,reverse
mesmo sereverse
implementarsort.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
reverse
estrutura.// 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.
Reverse
método aosort.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.
fonte
reverse
tenha um membro do tipoInterface
. Este membro então tem seus métodos chamáveis na estrutura externa ou substituíveis.extend
para 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 internoInterface
.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
sort
pacote: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
IntSlice
promoçãoreverse
.E isto:
type reverse struct { Interface }
significa que
sort.reverse
pode incorporar qualquer estrutura que implemente interfacesort.Interface
e quaisquer métodos que a interface possua, eles serão promovidosreverse
.sort.Interface
tem métodoLess(i, j int) bool
que 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"}) }
fonte
A declaração
type reverse struct { Interface }
permite que você inicialize
reverse
com tudo o que implementa a interfaceInterface
. Exemplo:&reverse{sort.Intslice([]int{1,2,3})}
Dessa forma, todos os métodos implementados pelo
Interface
valor incorporado são populados externamente enquanto você ainda é capaz de substituir alguns delesreverse
, por exemplo,Less
para 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 .fonte
Eu darei minha explicação também. O
sort
pacote define um tipo não exportadoreverse
, que é uma estrutura que incorporaInterface
.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
Less
método parareverse
chama oLess
método doInterface
valor 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) }
Len
eSwap
os outros dois métodos dereverse
, são fornecidos implicitamente peloInterface
valor original porque é um campo incorporado. AReverse
função exportada retorna uma instância doreverse
tipo que contém oInterface
valor original .// Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
fonte
Less
método parareverse
chama oLess
método doInterface
valor 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
Store
métodos. SóHealthCheck
pode ser simulado, já que apenas esse método é usado noTestIsHealthy
teste.Abaixo o resultado do
test
comando:$ 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
Store
interface: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 }
fonte