Arti dari struct dengan antarmuka anonim yang disematkan?

89

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?

warvariuc
sumber
Bagi pencari, ada penjelasan yang lebih sederhana di sini: Pandangan Lebih Dekat pada Golang Dari Perspektif Arsitek . Jangan biarkan judul artikel membuat Anda takut. :)
7stud
10
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.
func Reverse(data Interface) Interface {
        return &reverse{data}
}

Nilai sebenarnya muncul jika Anda berpikir apa yang harus Anda lakukan jika pendekatan ini tidak memungkinkan.

  1. Tambahkan Reversemetode lain ke sort.Interface?
  2. Buat ReverseInterface lain?
  3. ...?

Setiap perubahan ini akan membutuhkan lebih banyak baris kode di ribuan paket yang ingin menggunakan fungsi standar terbalik.

fabrizioM
sumber
3
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 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"})
}
warvariuc
sumber
3
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.

Inilah yang sebenarnya terjadi saat Anda menggunakan sort.Reverse. Anda dapat membaca tentang menyematkan di bagian struct spesifikasi .

nemo
sumber
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.
func Reverse(data Interface) Interface {
    return &reverse{data}
}
Endre Simo
sumber
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 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
}
czerasz.dll
sumber