Gömülü anonim arayüze sahip bir yapının anlamı nedir?


89

sort paket:

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

...

type reverse struct {
    Interface
}

Anonim arayüzünün anlamı nedir Interfaceyapı içinde reverse?


Araştırmacılar için, burada çok daha basit bir açıklama var: Bir Mimarın Perspektifinden Golang'a Daha Yakından Bir Bakış . Makalenin başlığının sizi korkutmasına izin vermeyin. :)
7

10
AIUI, o makale ("Daha Yakından Bakış ...") aslında anonim arayüzleri bir yapıya yerleştirmenin ne anlama geldiğinden bahsetmiyor, sadece genel olarak arayüzlerden bahsediyor.
Adrian Ludwin

Yanıtlar:


73

Bu şekilde, tersi uygular sort.Interfaceve diğerlerini tanımlamak zorunda kalmadan belirli bir yöntemi geçersiz kılabiliriz.

type reverse struct {
        // This embedded Interface permits Reverse to use the methods of
        // another Interface implementation.
        Interface
}

O swapları nasıl burada Bildirimi (j,i)yerine (i,j)yapı için ilan sadece yöntem ve ayrıca bu reversebile reverseuygulamaksort.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)
}

Bu yöntemin içinde hangi yapı geçirilirse aktarılsın, onu yeni bir reverseyapıya dönüştürüyoruz .

// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
        return &reverse{data}
}

Bu yaklaşım mümkün olmasaydı ne yapmanız gerektiğini düşünürseniz gerçek değer gelir.

  1. Başka ekle Reverseyöntemini sort.Interface?
  2. Başka bir ReverseInterface oluşturmak ister misiniz?
  3. ...?

Bu değişikliklerin herhangi biri, standart ters işlevselliği kullanmak isteyen binlerce paket içinde çok daha fazla kod satırı gerektirecektir.


3
yani bir arayüzün sadece bazı yöntemlerini yeniden tanımlamanıza izin veriyor mu?
David 天宇 Wong

2
Önemli olan reverse, tipte bir üyeye sahip olmasıdır Interface. Bu üye daha sonra dış yapı üzerinde çağrılabilir veya geçersiz kılınabilir yöntemlerine sahiptir.
Bryan

Bu özellik (veya yaklaşım) Java'da yaptığımız şeyi başarmanın bir yolu olarak düşünülebilir mi? extendsoyut olmayan alt sınıfları genişletmek için? Bana göre bu, dahili olarak uygulanan mevcut yöntemleri kullanırken yalnızca belirli yöntemleri geçersiz kılmanın kullanışlı bir yolu olabilir Interface.
Kevin Ghaboosi

Yani bu bir tür miras mı? Ve return r.Interface.Less(j, i)üst uygulamayı çağırmak mı?
warvariuc

Zaten 2. kez bu konuda kafam karıştı. Bütün cevaplar (aslında sıralama şey tabi) struct'ın unintuitive kullanımını unutmak gibi görünüyor: sort.Sort(sort.Reverse(sort.IntSlice(example))). Benim için buradaki acı nokta: Sıralama yöntemi ters yapıya yükseltilir, ancak çağrı üye olmayan (alıcı) stildir.
seebi

41

Tamam, kabul edilen cevap anlamama yardımcı oldu, ancak düşünme tarzıma daha uygun olduğunu düşündüğüm bir açıklama göndermeye karar verdim.

"Etkili git" gömülü diğer arayüzleri olan arayüzleri örnek vardır:

// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
    Reader
    Writer
}

ve diğer yapıları gömülü bir yapı:

// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

Ancak bir arayüzün gömülü olduğu bir yapıdan söz edilmiyor. Bunu sortpakette görünce kafam karıştı :

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

...

type reverse struct {
    Interface
}

Ancak fikir basit. Neredeyse aynı:

type reverse struct {
    IntSlice  // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}

IntSliceterfi etme yöntemleri reverse.

Ve bu:

type reverse struct {
    Interface
}

araçları sort.reverseuygular arayüzü herhangi struct'ı gömebilirsiniz sort.Interfaceve arayüz sahip olduğunu ne olursa olsun yöntemleri, onlar terfi edilecek reverse.

sort.InterfaceLess(i, j int) boolartık geçersiz kılınabilen bir yönteme sahiptir:

// 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)
}

Anlamaktaki kafa karışıklığım

type reverse struct {
    Interface
}

bir yapının her zaman sabit bir yapıya, yani sabit tipte sabit sayıda alana sahip olduğunu düşünmüştüm.

Ama şu yanlış olduğumu kanıtlıyor:

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"})
}

3
Anladığım doğruysa, arabirim değerleri kendisine atanan örneğe bir işaretçi ve örneğin türünün yöntem tablosuna bir işaretçi ile temsil edilir. Yani tüm arayüz değerleri bellekte aynı yapıya sahiptir. Yapısal olarak gömme, kompozisyon ile aynıdır. Dolayısıyla, bir arabirimi yerleştiren bir yapı bile sabit bir yapıya sahip olacaktır. Arayüzün işaret ettiği örneklerin yapıları farklı olacaktır.
Nishant George Agrwal

Bunu, çok daha fazla ayrıntı, net bir örnek ve belgelere bağlantı verdiği için kabul edilen yanıttan daha iyi buldum.
110100100

28

İfade

type reverse struct {
    Interface
}

reversearabirimi uygulayan her şeyi başlatmanızı sağlar Interface. Misal:

&reverse{sort.Intslice([]int{1,2,3})}

Bu şekilde, gömülü Interfacedeğer tarafından uygulanan tüm yöntemler reverse, örneğin Lesssıralamayı tersine çevirmek için bunlardan bazılarını geçersiz kılmaya devam ederken, dışarıya yerleştirilir .

Bu, kullandığınızda gerçekte olan şeydir sort.Reverse. Spesifikasyonun struct bölümüne yerleştirme hakkında bilgi edinebilirsiniz .


Zaten 2. kez bu konuda kafam karıştı. Bütün cevaplar (aslında sıralama şey tabi) struct'ın unintuitive kullanımını unutmak gibi görünüyor: sort.Sort(sort.Reverse(sort.IntSlice(example))). Benim için buradaki acı nokta: Sıralama yöntemi ters yapıya yükseltilir, ancak çağrı üye olmayan (alıcı) stildir.
seebi

@seebi eğer bu bir soru ise anlamadım, üzgünüm. Ayrıca Sortyöntem yükseltilmez, tatmin edici sort.Interfacebir şey alır ve tersine çevirir öyle bir şey, sadece gömülü sort.Interfaceverilerinin ilgili yöntemlerini değiştirir, böylece ortaya çıkan sıralama tersine çevrilir.
nemo

bir soru değil, sadece bir açıklama, haklısın Az, Takas, Len yöntemlerini kastettim!
seebi

7

Ben de açıklamamı vereceğim. sortPaket, unexported tür tanımlar reversebir yapı, bu gömme olup, Interface.

type reverse struct {
    // This embedded Interface permits Reverse to use the methods of
    // another Interface implementation.
    Interface
}

Bu, Reverse'nin başka bir Arayüz uygulamasının yöntemlerini kullanmasına izin verir. Bu sözde composition, Go'nun güçlü bir özelliğidir.

LessYöntemi reverseçağrıları Lessgömülü bir yöntem Interfacedeğeri ancak indisleri ile sıralama sonuçları sırası ters, ters çevrilmiş.

// 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)
}

Lenve Swapdiğer iki yöntem, gömülü bir alan olduğu reverseiçin orijinal Interfacedeğer tarafından örtük olarak sağlanır . Dışa aktarılan Reverseişlev reverse, orijinal Interfacedeğeri içeren türün bir örneğini döndürür .

// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
    return &reverse{data}
}

Bana göre bu miras gibi görünüyor. " LessYöntemi gömülü değerin yöntemini reverseçağırır , ancak endeksler ters çevrilerek sıralama sonuçlarının sırasını tersine çevirir." - bu, üst uygulamanın çağrılmasına benziyor. LessInterface
warvariuc

Ters
yazının

1

Yazarken ben çok yararlı bu özelliği bulmak mocks içinde testler .

İşte böyle bir örnek:

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")
    }
}

Kullanarak:

type storeMock struct {
    Store
    ...
}

Tüm Storeyöntemlerle dalga geçmeye gerek yok . Sadece HealthChecktek bu yöntem kullanıldığı için, alay edilebilir TestIsHealthytesti.

testKomutun sonucunun altında :

$ go test -run '^TestIsHealthy$' ./main_test.go           
ok      command-line-arguments  0.003s

Bir gerçek dünya örneği test ederken bu kullanım örneği birinin bulabilirsiniz AWS SDK .


Daha da açık hale getirmek için, işte çirkin alternatif - Storearayüzü tatmin etmek için uygulanması gereken minimum alternatif :

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.