sort paket:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Apa arti antarmuka anonim Interfacedi struct reverse?
sort paket:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Apa arti antarmuka anonim Interfacedi struct reverse?
Jawaban:
Dengan cara ini, reverse mengimplementasikan sort.Interfacedan kita dapat menimpa metode tertentu tanpa harus mendefinisikan yang lainnya
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Perhatikan bagaimana di sini ia menukar (j,i)alih-alih (i,j)dan juga ini adalah satu-satunya metode yang dideklarasikan untuk struct reversemeskipun reversediimplementasikansort.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)
}
Struct apa pun yang dilewatkan di dalam metode ini kami mengubahnya menjadi reversestruct baru .
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Nilai sebenarnya muncul jika Anda berpikir apa yang harus Anda lakukan jika pendekatan ini tidak memungkinkan.
Reversemetode lain ke sort.Interface?Setiap perubahan ini akan membutuhkan lebih banyak baris kode di ribuan paket yang ingin menggunakan fungsi standar terbalik.
reversememiliki anggota dari jenis Interface. Anggota ini kemudian memiliki metodenya yang dapat dipanggil di struct luar, atau dapat diganti.
extenduntuk memperluas sub-kelas non-abstrak? Bagi saya, ini bisa menjadi cara praktis untuk menimpa hanya metode tertentu saat menggunakan metode yang sudah ada yang diimplementasikan oleh internal Interface.
return r.Interface.Less(j, i)apakah memanggil implementasi induk?
sort.Sort(sort.Reverse(sort.IntSlice(example))). Bagi saya titik sakitnya di sini: metode Sort dipromosikan ke struct terbalik, tetapi panggilannya adalah gaya non-anggota (penerima).
Oke, jawaban yang diterima membantu saya memahami, tetapi saya memutuskan untuk memposting penjelasan yang menurut saya lebih sesuai dengan cara berpikir saya.
The "Effective Go" memiliki contoh antarmuka yang memiliki antarmuka lain yang disematkan:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
dan struct yang memiliki struct lain:
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Tetapi tidak disebutkan bahwa struct memiliki antarmuka yang tertanam. Saya bingung melihat ini dalam sortpaket:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Tapi idenya sederhana. Hampir sama dengan:
type reverse struct {
IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}
metode IntSliceuntuk dipromosikanreverse .
Dan ini:
type reverse struct {
Interface
}
berarti sort.reversedapat menyematkan struct apa pun yang mengimplementasikan antarmukasort.Interface dan metode apa pun yang dimiliki antarmuka, mereka akan dipromosikanreverse .
sort.Interfacememiliki metode Less(i, j int) boolyang sekarang dapat diganti:
// 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)
}
Kebingungan saya dalam memahami
type reverse struct {
Interface
}
adalah bahwa saya berpikir bahwa struct selalu memiliki struktur tetap, yaitu jumlah bidang tetap dari tipe tetap.
Tapi hal berikut membuktikan saya salah:
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"})
}
Pernyataan
type reverse struct {
Interface
}
memungkinkan Anda untuk menginisialisasi reversedengan semua yang mengimplementasikan antarmuka Interface. Contoh:
&reverse{sort.Intslice([]int{1,2,3})}
Dengan cara ini, semua metode yang diimplementasikan oleh nilai yang disematkan Interfaceakan diisi ke luar sementara Anda masih dapat menimpa beberapa di antaranya di reverse, misalnyaLess untuk membalikkan pengurutan.
Inilah yang sebenarnya terjadi saat Anda menggunakan sort.Reverse. Anda dapat membaca tentang menyematkan di bagian struct spesifikasi .
sort.Sort(sort.Reverse(sort.IntSlice(example))). Bagi saya titik sakitnya di sini: metode Sort dipromosikan ke struct terbalik, tetapi panggilannya adalah gaya non-anggota (penerima).
Sortmetode tidak dipromosikan, dibutuhkan sesuatu yang memuaskan sort.Interfacedan sebaliknya adalah hal seperti itu, hanya mengubah metode yang relevan dari sort.Interfacedata yang disematkan sehingga jenis yang dihasilkan dibalik.
Saya akan memberikan penjelasan saya juga. The sortpaket mendefinisikan jenis unexported reverse, yang merupakan struct, yang embeds Interface.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Ini memungkinkan Reverse untuk menggunakan metode implementasi Antarmuka lain. Inilah yang disebutcomposition , yang merupakan fitur hebat dari Go.
The Lessmetode untuk reversepanggilan Lessmetode tertanam Interfacenilai, tetapi dengan indeks membalik, membalik urutan hasil semacam.
// 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)
}
Lendan Swapdua metode lainnya reverse, secara implisit disediakan oleh nilai asli Interfacekarena merupakan bidang tersemat. Fungsi yang diekspor Reversemengembalikan contoh reversetipe yang berisi nilai asli Interface.
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
LessMetode untuk reversememanggil Lessmetode nilai yang disematkan Interface, tetapi dengan indeks dibalik, membalik urutan hasil pengurutan." - ini terlihat seperti pemanggilan implementasi induk.
Saya menemukan fitur ini sangat membantu saat menulis ejekan dalam tes .
Inilah contohnya:
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")
}
}
Dengan menggunakan:
type storeMock struct {
Store
...
}
Seseorang tidak perlu mengejek semua Storemetode. Hanya HealthCheckdapat diolok-olok, karena hanya metode ini yang digunakan dalam TestIsHealthypengujian.
Di bawah hasil testperintah:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
Contoh dunia nyata dari kasus penggunaan ini dapat ditemukan saat menguji AWS SDK .
Untuk membuatnya lebih jelas, berikut adalah alternatif jelek - minimum yang perlu diterapkan untuk memenuhi Storeantarmuka:
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
}