sort paquet:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Quelle est la signification de l'interface anonyme Interfacedans struct reverse?
sort paquet:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Quelle est la signification de l'interface anonyme Interfacedans struct reverse?
Réponses:
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.
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.
Reverseméthode au sort.Interface?Chacune de ces modifications nécessiterait de nombreuses autres lignes de code sur des milliers de packages souhaitant utiliser la fonctionnalité inverse standard.
reversea un membre de type Interface. Ce membre a alors ses méthodes appelables sur la structure externe ou remplaçables.
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.
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 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 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 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.
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 .
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.
func Reverse(data Interface) Interface {
return &reverse{data}
}
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.
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 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:
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
}