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 Interfacew 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 Interfacew strukturze reverse?
Odpowiedzi:
W ten sposób reverse implementuje sort.Interfacei 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, reversenawet jeśli reverseimplementujesort.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ą reversestrukturę.
// 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.
Reversemetodę 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.
reversema typ członkaInterface . Ten element członkowski ma następnie metody wywoływane w zewnętrznej strukturze lub nadpisywane.
extenddo 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 sortpakiecie:
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 IntSliceawansu reverse.
I to:
type reverse struct {
Interface
}
oznacza, że sort.reversemoże osadzić dowolną strukturę, która implementuje interfejs sort.Interfacei wszelkie metody, które ma ten interfejs, zostaną promowane reverse.
sort.Interfacema metodę, Less(i, j int) boolktó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 reversewszystkiego, co implementuje interfejs Interface. Przykład:
&reverse{sort.Intslice([]int{1,2,3})}
W ten sposób wszystkie metody implementowane przez Interfacewartość osadzoną są wypełniane na zewnątrz, podczas gdy nadal możesz zastąpić niektóre z nich reverse, na przykład w Lesscelu 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. sortPakiet 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.
LessMetoda reversepołączeń Systemy Lessmetoda wbudowanego Interfacewartoś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)
}
Lena Swappozostałe dwie metody reversesą niejawnie dostarczane przez oryginalną Interfacewartość, ponieważ jest to pole osadzone. Wyeksportowana Reversefunkcja zwraca instancję reversetypu, który zawiera oryginalną Interfacewartość.
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
LessMetoda reversewywołuje Lessmetodę Interfacewartoś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 Storemetod. HealthCheckMożna tylko wyśmiać, ponieważ w TestIsHealthyteście jest używana tylko ta metoda .
Poniżej wynik testpolecenia:
$ 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ć Storeinterfejs:
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
}