AIUI, artikel itu ("A Closer Look ...") sebenarnya tidak berbicara tentang apa artinya menyematkan antarmuka anonim ke dalam struct, itu hanya berbicara tentang antarmuka secara umum.
Adrian Ludwin
Jawaban:
73
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.funcReverse(data Interface)Interface {
return &reverse{data}
}
Nilai sebenarnya muncul jika Anda berpikir apa yang harus Anda lakukan jika pendekatan ini tidak memungkinkan.
Tambahkan Reversemetode lain ke sort.Interface?
Buat ReverseInterface lain?
...?
Setiap perubahan ini akan membutuhkan lebih banyak baris kode di ribuan paket yang ingin menggunakan fungsi standar terbalik.
sehingga memungkinkan Anda untuk mendefinisikan kembali hanya beberapa metode antarmuka?
David 天宇 Wong
2
Bagian penting adalah bahwa reversememiliki anggota dari jenis Interface. Anggota ini kemudian memiliki metodenya yang dapat dipanggil di struct luar, atau dapat diganti.
Bryan
Mungkinkah fitur (atau pendekatan) ini dianggap sebagai cara untuk mencapai apa yang kita lakukan di Java melalui. 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.
Kevin Ghaboosi
Jadi ini semacam warisan? Dan return r.Interface.Less(j, i)apakah memanggil implementasi induk?
warvariuc
Udah ke-2 kalinya gw bingung soal ini. Semua jawaban tampaknya lupa penggunaan unintuitive dari struct ini (yang diperlukan untuk benar-benar semacam apa-apa): 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).
seebi
41
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 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"})
}
Jika pemahaman saya benar, nilai antarmuka diwakili oleh sebuah pointer ke instance yang ditugaskan padanya dan pointer ke tabel metode dari tipe instance tersebut. Jadi semua nilai antarmuka memiliki struktur yang sama di memori. Secara struktural, penyematan sama dengan komposisi. Jadi, bahkan struct yang menyematkan antarmuka akan memiliki struktur tetap. Struktur instance yang ditunjukkan antarmuka akan berbeda.
Nishant George Agrwal
Saya menemukan ini jawaban yang lebih baik daripada yang diterima karena memberikan lebih banyak detail, contoh yang jelas dan tautan ke dokumentasi.
110100100
28
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.
Udah ke-2 kalinya gw bingung soal ini. Semua jawaban tampaknya lupa penggunaan unintuitive dari struct ini (yang diperlukan untuk benar-benar semacam apa-apa): 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).
seebi
@seebi jika ini adalah pertanyaan saya tidak mengerti, maaf. Juga 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.
nemo
bukan pertanyaan, hanya komentar, Anda benar maksud saya metode Less, Swap, Len!
seebi
7
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.funcReverse(data Interface)Interface {
return &reverse{data}
}
Bagi saya ini tampak seperti warisan. " LessMetode untuk reversememanggil Lessmetode nilai yang disematkan Interface, tetapi dengan indeks dibalik, membalik urutan hasil pengurutan." - ini terlihat seperti pemanggilan implementasi induk.
warvariuc
Selama tipe reverse hanya memiliki satu bidang yang mengimplementasikan antarmuka Antarmuka, ia juga menjadi anggota antarmuka Antarmuka: 0
Allan Guwatudde
1
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 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")
}
}
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:
Jawaban:
Dengan cara ini, reverse mengimplementasikan
sort.Interface
dan kita dapat menimpa metode tertentu tanpa harus mendefinisikan yang lainnyatype 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 structreverse
meskipunreverse
diimplementasikansort.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
reverse
struct 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.
Reverse
metode lain kesort.Interface
?Setiap perubahan ini akan membutuhkan lebih banyak baris kode di ribuan paket yang ingin menggunakan fungsi standar terbalik.
sumber
reverse
memiliki anggota dari jenisInterface
. Anggota ini kemudian memiliki metodenya yang dapat dipanggil di struct luar, atau dapat diganti.extend
untuk 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 internalInterface
.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
sort
paket: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
IntSlice
untuk dipromosikanreverse
.Dan ini:
type reverse struct { Interface }
berarti
sort.reverse
dapat menyematkan struct apa pun yang mengimplementasikan antarmukasort.Interface
dan metode apa pun yang dimiliki antarmuka, mereka akan dipromosikanreverse
.sort.Interface
memiliki metodeLess(i, j int) bool
yang 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"}) }
sumber
Pernyataan
type reverse struct { Interface }
memungkinkan Anda untuk menginisialisasi
reverse
dengan semua yang mengimplementasikan antarmukaInterface
. Contoh:&reverse{sort.Intslice([]int{1,2,3})}
Dengan cara ini, semua metode yang diimplementasikan oleh nilai yang disematkan
Interface
akan diisi ke luar sementara Anda masih dapat menimpa beberapa di antaranya direverse
, misalnyaLess
untuk membalikkan pengurutan.Inilah yang sebenarnya terjadi saat Anda menggunakan
sort.Reverse
. Anda dapat membaca tentang menyematkan di bagian struct spesifikasi .sumber
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).Sort
metode tidak dipromosikan, dibutuhkan sesuatu yang memuaskansort.Interface
dan sebaliknya adalah hal seperti itu, hanya mengubah metode yang relevan darisort.Interface
data yang disematkan sehingga jenis yang dihasilkan dibalik.Saya akan memberikan penjelasan saya juga. The
sort
paket mendefinisikan jenis unexportedreverse
, yang merupakan struct, yang embedsInterface
.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 disebut
composition
, yang merupakan fitur hebat dari Go.The
Less
metode untukreverse
panggilanLess
metode tertanamInterface
nilai, 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) }
Len
danSwap
dua metode lainnyareverse
, secara implisit disediakan oleh nilai asliInterface
karena merupakan bidang tersemat. Fungsi yang dieksporReverse
mengembalikan contohreverse
tipe yang berisi nilai asliInterface
.// Reverse returns the reverse order for data. func Reverse(data Interface) Interface { return &reverse{data} }
sumber
Less
Metode untukreverse
memanggilLess
metode nilai yang disematkanInterface
, 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
Store
metode. HanyaHealthCheck
dapat diolok-olok, karena hanya metode ini yang digunakan dalamTestIsHealthy
pengujian.Di bawah hasil
test
perintah:$ 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
Store
antarmuka: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 }
sumber