AIUI, ten artykuł ("Bliższe spojrzenie ...") tak naprawdę nie mówi o tym, co to znaczy osadzać anonimowe interfejsy w strukturze, tylko ogólnie mówi o interfejsach.
Adrian Ludwin
Odpowiedzi:
67
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.funcReverse(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.
Dodaj inną Reversemetodę do sort.Interface?
Utworzyć kolejny ReverseInterface?
...?
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.
więc pozwala na przedefiniowanie tylko niektórych metod interfejsu?
David 天宇 Wong
1
Ważną częścią jest to, że reversema typ członkaInterface . Ten element członkowski ma następnie metody wywoływane w zewnętrznej strukturze lub nadpisywane.
Bryan
Czy tę funkcję (lub podejście) można uznać za sposób na osiągnięcie tego, co robimy w Javie przez. 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.
Kevin Ghaboosi,
Więc to rodzaj spadku? I return r.Interface.Less(j, i)czy wywołuje implementację nadrzędną?
warvariuc
39
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 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"})
}
Jeśli rozumiem poprawnie, wartości interfejsu są reprezentowane przez wskaźnik do przypisanej do niego instancji oraz wskaźnik do tabeli metod typu instancji. Zatem wszystkie wartości interfejsu mają tę samą strukturę w pamięci. Strukturalnie osadzanie jest tym samym, co kompozycja. Zatem nawet struktura osadzająca interfejs miałaby stałą strukturę. Struktury instancji wskazywanych przez interfejs będą się różnić.
Nishant George Agrwal
Uznałem, że jest to lepsza odpowiedź niż zaakceptowana, ponieważ zawierała znacznie więcej szczegółów, jasny przykład i link do dokumentacji.
110100100
25
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.
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.funcReverse(data Interface)Interface {
return &reverse{data}
}
Dla mnie wygląda to na dziedziczenie. „ 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.
warvariuc
Tak długo, jak typ reverse ma tylko jedno pole, które implementuje interfejs interfejsu, staje się również członkiem interfejsu interfejsu: 0
Allan Guwatudde
1
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 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")
}
}
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:
Odpowiedzi:
W ten sposób reverse implementuje
sort.Interface
i możemy przesłonić określoną metodę bez konieczności definiowania wszystkich innychtype 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ślireverse
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ę dosort.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.
źródło
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
awansureverse
.I to:
type reverse struct { Interface }
oznacza, że
sort.reverse
może osadzić dowolną strukturę, która implementuje interfejssort.Interface
i wszelkie metody, które ma ten interfejs, zostaną promowanereverse
.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"}) }
źródło
Wyrok
type reverse struct { Interface }
umożliwia zainicjowanie
reverse
wszystkiego, co implementuje interfejsInterface
. 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 nichreverse
, na przykład wLess
celu odwrócenia sortowania.Tak właśnie się dzieje, gdy używasz
sort.Reverse
. Możesz przeczytać o osadzaniu w sekcji struct specyfikacji .źródło
Podam również moje wyjaśnienie.
sort
Pakiet określa unexported typureverse
, który jest struktura, osadzającejInterface
.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
Metodareverse
połączeń SystemyLess
metoda wbudowanegoInterface
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
aSwap
pozostałe dwie metodyreverse
są niejawnie dostarczane przez oryginalnąInterface
wartość, ponieważ jest to pole osadzone. WyeksportowanaReverse
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} }
źródło
Less
Metodareverse
wywołujeLess
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ż wTestIsHealthy
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 }
źródło