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 Interface
dans 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 Interface
dans struct reverse
?
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 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 reverse
même si reverse
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 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.
reverse
a un membre de type Interface
. 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 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 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
promotion reverse
.
Et ça:
type reverse struct {
Interface
}
signifie que sort.reverse
peut incorporer n'importe quelle structure qui implémente l'interface sort.Interface
et quelles que soient les méthodes de cette interface, elles seront promues reverse
.
sort.Interface
a une méthode Less(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 déclaration
type reverse struct {
Interface
}
vous permet d'initialiser reverse
avec 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 Interface
valeur incorporée sont remplies vers l'extérieur tandis que vous pouvez toujours en remplacer certaines reverse
, par exemple Less
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 .
Je donnerai aussi mon explication. Le sort
package 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 Less
méthode pour reverse
appelle la Less
méthode de la Interface
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
et Swap
les deux autres méthodes de reverse
, sont implicitement fournies par la Interface
valeur d' origine car il s'agit d'un champ incorporé. La Reverse
fonction exportée renvoie une instance du reverse
type qui contient la Interface
valeur d' origine .
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Less
méthode pour reverse
appelle la Less
méthode de la Interface
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. Seul HealthCheck
peut être moqué, puisque seule cette méthode est utilisée dans le TestIsHealthy
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
}