AIUI, cet article ("A Closer Look ...") ne parle pas réellement de ce que signifie intégrer des interfaces anonymes dans une structure, il parle simplement des interfaces en général.
Adrian Ludwin
Réponses:
67
De cette façon, reverse implémente sort.Interfaceet nous pouvons remplacer une méthode spécifique sans avoir à définir toutes les autres
type reverse struct {
// This embedded Interface permits Reverse to use the methods of// another Interface implementation.
Interface
}
Remarquez comment ici il échange (j,i)au lieu de (i,j)et c'est aussi la seule méthode déclarée pour la structure reversemême si reverseimplémentersort.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)
}
Quelle que soit la structure passée dans cette méthode, nous la convertissons en une nouvelle reversestructure.
// Reverse returns the reverse order for data.funcReverse(data Interface)Interface {
return &reverse{data}
}
La vraie valeur vient si vous pensez à ce que vous auriez à faire si cette approche n'était pas possible.
Ajoutez une autre Reverseméthode au sort.Interface?
Créer une autre ReverseInterface?
...?
Chacune de ces modifications nécessiterait de nombreuses autres lignes de code sur des milliers de packages souhaitant utiliser la fonctionnalité inverse standard.
donc cela vous permet de redéfinir juste certaines des méthodes d'une interface?
David 天宇 Wong
1
La partie importante est qu'il reversea un membre de type Interface. Ce membre a alors ses méthodes appelables sur la structure externe ou remplaçables.
Bryan
Cette fonctionnalité (ou approche) pourrait-elle être considérée comme un moyen de réaliser ce que nous faisons en Java via. extendpour étendre des sous-classes non abstraites? Pour moi, cela peut être un moyen pratique de remplacer uniquement certaines méthodes tout en utilisant les méthodes existantes implémentées par internal Interface.
Kevin Ghaboosi
C'est donc une sorte d'héritage? Et return r.Interface.Less(j, i)appelle-t-on l'implémentation parent?
warvariuc
39
Ok, la réponse acceptée m'a aidé à comprendre, mais j'ai décidé de poster une explication qui, je pense, convient mieux à ma façon de penser.
Le "Effective Go" a des exemples d'interfaces ayant intégré d'autres interfaces:
// ReadWriter is the interface that combines the Reader and Writer interfaces.type ReadWriter interface {
Reader
Writer
}
et une structure ayant incorporé d'autres structures:
// ReadWriter stores pointers to a Reader and a Writer.// It implements io.ReadWriter.type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Mais il n'y a aucune mention d'une structure ayant intégré une interface. J'étais confus en voyant cela dans le sortpackage:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Mais l'idée est simple. C'est presque la même chose que:
type reverse struct {
IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}
méthodes de IntSlicepromotion reverse.
Et ça:
type reverse struct {
Interface
}
signifie que sort.reversepeut incorporer n'importe quelle structure qui implémente l'interface sort.Interfaceet quelles que soient les méthodes de cette interface, elles seront promues reverse.
sort.Interfacea une méthode Less(i, j int) boolqui peut maintenant être remplacée:
// 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)
}
Ma confusion dans la compréhension
type reverse struct {
Interface
}
était que je pensais qu'une structure a toujours une structure fixe, c'est-à-dire un nombre fixe de champs de types fixes.
Mais ce qui suit me prouve mal:
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"})
}
Si ma compréhension est correcte, les valeurs d'interface sont représentées par un pointeur vers l'instance qui lui est assignée et un pointeur vers la table de méthode du type de l'instance. Ainsi, toutes les valeurs d'interface ont la même structure en mémoire. Structurellement, l'intégration est la même que la composition. Ainsi, même une structure intégrant une interface aurait une structure fixe. Les structures des instances vers lesquelles pointe l'interface seront différentes.
Nishant George Agrwal
J'ai trouvé que c'était une meilleure réponse que celle acceptée car elle donnait beaucoup plus de détails, un exemple clair et un lien vers la documentation.
110100100 le
25
La déclaration
type reverse struct {
Interface
}
vous permet d'initialiser reverseavec tout ce qui implémente l'interface Interface. Exemple:
&reverse{sort.Intslice([]int{1,2,3})}
De cette façon, toutes les méthodes implémentées par la Interfacevaleur incorporée sont remplies vers l'extérieur tandis que vous pouvez toujours en remplacer certaines reverse, par exemple Lesspour inverser le tri.
Je donnerai aussi mon explication. Le sortpackage définit un type non exporté reverse, qui est une structure, qui incorpore Interface.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of// another Interface implementation.
Interface
}
Cela permet à Reverse d'utiliser les méthodes d'une autre implémentation d'interface. C'est ce qu'on appelle composition, qui est une fonctionnalité puissante de Go.
La Lessméthode pour reverseappelle la Lessméthode de la Interfacevaleur incorporée , mais avec les indices inversés, inversant l'ordre des résultats de tri.
// 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)
}
Lenet Swaples deux autres méthodes de reverse, sont implicitement fournies par la Interfacevaleur d' origine car il s'agit d'un champ incorporé. La Reversefonction exportée renvoie une instance du reversetype qui contient la Interfacevaleur d' origine .
// Reverse returns the reverse order for data.funcReverse(data Interface)Interface {
return &reverse{data}
}
Pour moi, cela ressemble à un héritage. "La Lessméthode pour reverseappelle la Lessméthode de la Interfacevaleur intégrée , mais avec les indices inversés, inversant l'ordre des résultats de tri." - cela ressemble à l'appel de l'implémentation parent.
warvariuc
Tant que le type reverse n'a qu'un seul champ qui implémente l'interface d'interface, il devient également membre de l'interface d'interface: 0
Allan Guwatudde
1
Je trouve cette fonctionnalité très utile lors de l'écriture de simulations dans les tests .
Voici un exemple:
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")
}
}
En utilisant:
type storeMock struct {
Store
...
}
Il n'est pas nécessaire de se moquer de toutes les Storeméthodes. Seul HealthCheckpeut être moqué, puisque seule cette méthode est utilisée dans le TestIsHealthytest.
Ci-dessous le résultat de la testcommande:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
Un exemple concret de ce cas d'utilisation que l'on peut trouver lors du test du kit AWS SDK .
Pour rendre cela encore plus évident, voici l'alternative laide - le minimum qu'il faut implémenter pour satisfaire l' Storeinterface:
Réponses:
De cette façon, reverse implémente
sort.Interface
et nous pouvons remplacer une méthode spécifique sans avoir à définir toutes les autrestype reverse struct { // This embedded Interface permits Reverse to use the methods of // another Interface implementation. Interface }
Remarquez comment ici il échange
(j,i)
au lieu de(i,j)
et c'est aussi la seule méthode déclarée pour la structurereverse
même sireverse
implémentersort.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) }
Quelle que soit la structure passée dans cette méthode, nous la convertissons en une nouvelle
reverse
structure.// Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
La vraie valeur vient si vous pensez à ce que vous auriez à faire si cette approche n'était pas possible.
Reverse
méthode ausort.Interface
?Chacune de ces modifications nécessiterait de nombreuses autres lignes de code sur des milliers de packages souhaitant utiliser la fonctionnalité inverse standard.
la source
reverse
a un membre de typeInterface
. Ce membre a alors ses méthodes appelables sur la structure externe ou remplaçables.extend
pour étendre des sous-classes non abstraites? Pour moi, cela peut être un moyen pratique de remplacer uniquement certaines méthodes tout en utilisant les méthodes existantes implémentées par internalInterface
.return r.Interface.Less(j, i)
appelle-t-on l'implémentation parent?Ok, la réponse acceptée m'a aidé à comprendre, mais j'ai décidé de poster une explication qui, je pense, convient mieux à ma façon de penser.
Le "Effective Go" a des exemples d'interfaces ayant intégré d'autres interfaces:
// ReadWriter is the interface that combines the Reader and Writer interfaces. type ReadWriter interface { Reader Writer }
et une structure ayant incorporé d'autres structures:
// ReadWriter stores pointers to a Reader and a Writer. // It implements io.ReadWriter. type ReadWriter struct { *Reader // *bufio.Reader *Writer // *bufio.Writer }
Mais il n'y a aucune mention d'une structure ayant intégré une interface. J'étais confus en voyant cela dans le
sort
package:type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) } ... type reverse struct { Interface }
Mais l'idée est simple. C'est presque la même chose que:
type reverse struct { IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order }
méthodes de
IntSlice
promotionreverse
.Et ça:
type reverse struct { Interface }
signifie que
sort.reverse
peut incorporer n'importe quelle structure qui implémente l'interfacesort.Interface
et quelles que soient les méthodes de cette interface, elles seront promuesreverse
.sort.Interface
a une méthodeLess(i, j int) bool
qui peut maintenant être remplacée:// 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) }
Ma confusion dans la compréhension
type reverse struct { Interface }
était que je pensais qu'une structure a toujours une structure fixe, c'est-à-dire un nombre fixe de champs de types fixes.
Mais ce qui suit me prouve mal:
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"}) }
la source
La déclaration
type reverse struct { Interface }
vous permet d'initialiser
reverse
avec tout ce qui implémente l'interfaceInterface
. Exemple:&reverse{sort.Intslice([]int{1,2,3})}
De cette façon, toutes les méthodes implémentées par la
Interface
valeur incorporée sont remplies vers l'extérieur tandis que vous pouvez toujours en remplacer certainesreverse
, par exempleLess
pour inverser le tri.C'est ce qui se passe réellement lorsque vous utilisez
sort.Reverse
. Vous pouvez en savoir plus sur l'incorporation dans la section struct de la spécification .la source
Je donnerai aussi mon explication. Le
sort
package définit un type non exportéreverse
, qui est une structure, qui incorporeInterface
.type reverse struct { // This embedded Interface permits Reverse to use the methods of // another Interface implementation. Interface }
Cela permet à Reverse d'utiliser les méthodes d'une autre implémentation d'interface. C'est ce qu'on appelle
composition
, qui est une fonctionnalité puissante de Go.La
Less
méthode pourreverse
appelle laLess
méthode de laInterface
valeur incorporée , mais avec les indices inversés, inversant l'ordre des résultats de tri.// 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
etSwap
les deux autres méthodes dereverse
, sont implicitement fournies par laInterface
valeur d' origine car il s'agit d'un champ incorporé. LaReverse
fonction exportée renvoie une instance dureverse
type qui contient laInterface
valeur d' origine .// Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
la source
Less
méthode pourreverse
appelle laLess
méthode de laInterface
valeur intégrée , mais avec les indices inversés, inversant l'ordre des résultats de tri." - cela ressemble à l'appel de l'implémentation parent.Je trouve cette fonctionnalité très utile lors de l'écriture de simulations dans les tests .
Voici un exemple:
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") } }
En utilisant:
type storeMock struct { Store ... }
Il n'est pas nécessaire de se moquer de toutes les
Store
méthodes. SeulHealthCheck
peut être moqué, puisque seule cette méthode est utilisée dans leTestIsHealthy
test.Ci-dessous le résultat de la
test
commande:$ go test -run '^TestIsHealthy$' ./main_test.go ok command-line-arguments 0.003s
Un exemple concret de ce cas d'utilisation que l'on peut trouver lors du test du kit AWS SDK .
Pour rendre cela encore plus évident, voici l'alternative laide - le minimum qu'il faut implémenter pour satisfaire l'
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 }
la source