sort
pakiet:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Jakie jest znaczenie anonimowego interfejsu Interface
w strukturze reverse
?
sort
pakiet:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Jakie jest znaczenie anonimowego interfejsu Interface
w strukturze reverse
?
Odpowiedzi:
W ten sposób reverse implementuje sort.Interface
i możemy przesłonić określoną metodę bez konieczności definiowania wszystkich innych
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Zwróć uwagę, jak tutaj zamienia się (j,i)
zamiast, (i,j)
a także jest to jedyna metoda zadeklarowana dla struktury, reverse
nawet jeśli reverse
implementujesort.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)
}
Cokolwiek struktura jest przekazywana w tej metodzie, konwertujemy ją na nową reverse
strukturę.
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Prawdziwa wartość pojawia się, jeśli pomyślisz, co byś musiał zrobić, gdyby takie podejście nie było możliwe.
Reverse
metodę do sort.Interface
?Każda z tych zmian wymagałaby o wiele więcej wierszy kodu w tysiącach pakietów, które chcą korzystać ze standardowej funkcji odwrotnej.
reverse
ma typ członkaInterface
. Ten element członkowski ma następnie metody wywoływane w zewnętrznej strukturze lub nadpisywane.
extend
do rozszerzania nieabstrakcyjnych podklas? Dla mnie może to być wygodny sposób na zastąpienie tylko niektórych metod przy użyciu istniejących, które są implementowane przez wewnętrzną Interface
.
return r.Interface.Less(j, i)
czy wywołuje implementację nadrzędną?
Ok, zaakceptowana odpowiedź pomogła mi zrozumieć, ale zdecydowałem się zamieścić wyjaśnienie, które moim zdaniem lepiej pasuje do mojego sposobu myślenia.
„Efektywne Go” ma przykład interfejsów mających osadzone innych interfejsów:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
oraz struktura mająca osadzone inne struktury:
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Ale nie ma wzmianki o strukturze mającej osadzony interfejs. Byłem zdezorientowany widząc to w sort
pakiecie:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Ale pomysł jest prosty. To prawie to samo, co:
type reverse struct {
IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}
metody IntSlice
awansu reverse
.
I to:
type reverse struct {
Interface
}
oznacza, że sort.reverse
może osadzić dowolną strukturę, która implementuje interfejs sort.Interface
i wszelkie metody, które ma ten interfejs, zostaną promowane reverse
.
sort.Interface
ma metodę, Less(i, j int) bool
którą można teraz nadpisać:
// 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)
}
Moje zamieszanie w zrozumieniu
type reverse struct {
Interface
}
polegało na tym, że myślałem, że struktura zawsze ma ustaloną strukturę, czyli stałą liczbę pól o ustalonych typach.
Ale poniższe udowadniają, że się mylę:
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"})
}
Wyrok
type reverse struct {
Interface
}
umożliwia zainicjowanie reverse
wszystkiego, co implementuje interfejs Interface
. Przykład:
&reverse{sort.Intslice([]int{1,2,3})}
W ten sposób wszystkie metody implementowane przez Interface
wartość osadzoną są wypełniane na zewnątrz, podczas gdy nadal możesz zastąpić niektóre z nich reverse
, na przykład w Less
celu odwrócenia sortowania.
Tak właśnie się dzieje, gdy używasz sort.Reverse
. Możesz przeczytać o osadzaniu w sekcji struct specyfikacji .
Podam również moje wyjaśnienie. sort
Pakiet określa unexported typu reverse
, który jest struktura, osadzającej Interface
.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Pozwala to Reverse na użycie metod innej implementacji interfejsu. Jest to tak zwane composition
, co jest potężną funkcją Go.
Less
Metoda reverse
połączeń Systemy Less
metoda wbudowanego Interface
wartości, ale z indeksami odwrócenie, odwrócenie kolejności sortowania wyników.
// 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
a Swap
pozostałe dwie metody reverse
są niejawnie dostarczane przez oryginalną Interface
wartość, ponieważ jest to pole osadzone. Wyeksportowana Reverse
funkcja zwraca instancję reverse
typu, który zawiera oryginalną Interface
wartość.
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Less
Metoda reverse
wywołuje Less
metodę Interface
wartości osadzonej , ale z odwróconymi indeksami, odwraca kolejność wyników sortowania”. - wygląda to na wywołanie implementacji nadrzędnej.
Uważam, że ta funkcja jest bardzo pomocna podczas pisania makiet w testach .
Oto taki przykład:
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")
}
}
Używając:
type storeMock struct {
Store
...
}
Nie trzeba kpić ze wszystkich Store
metod. HealthCheck
Można tylko wyśmiać, ponieważ w TestIsHealthy
teście jest używana tylko ta metoda .
Poniżej wynik test
polecenia:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
Prawdziwym przykładem świat tego użyć jednego przypadku można odnaleźć podczas testowania AWS SDK .
Aby było to jeszcze bardziej oczywiste, oto brzydka alternatywa - minimum, które należy zaimplementować, aby zadowolić Store
interfejs:
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
}