匿名インターフェースが埋め込まれた構造体の意味は?


89

sort パッケージ:

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

...

type reverse struct {
    Interface
}

Interface構造体の匿名インターフェースの意味は何reverseですか?


検索者のために、ここにはるかに簡単な説明があります:建築家の視点からのGolangの詳細。記事のタイトルがあなたを怖がらせないでください。:)
7stud 2017年

10
AIUI、その記事( "A Closer Look ...")は、匿名インターフェイスを構造体に埋め込むことの意味については実際には説明していません。インターフェイス全般について説明しているだけです。
エイドリアンラドウィン2018

回答:


73

このようにして、リバースはを実装しsort.Interface、他のすべてを定義することなく特定のメソッドをオーバーライドできます

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

ここでは、(j,i)代わりにスワップする方法に注意してください。(i,j)またreversereverse実装した場合でも、これが構造体に対して宣言された唯一のメソッドです。sort.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)
}

このメソッド内で渡される構造体はreverseすべて、新しい構造体に変換します。

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

このアプローチが不可能だった場合に何をしなければならないかを考えると、真の価値が生まれます。

  1. ?に別のReverseメソッドを追加しsort.Interfaceます
  2. 別のReverseInterfaceを作成しますか?
  3. ...?

この変更には、標準のリバース機能を使用する数千のパッケージにわたって、さらに多くのコード行が必要になります。


3
だからそれはあなたがインターフェースのメソッドのほんの一部を再定義することを可能にしますか?
デビッド天宇ウォン2017

2
重要な部分は、ということであるreverse持っているメンバーのタイプのをInterface。このメンバーのメソッドは、外部構造体で呼び出し可能、またはオーバーライド可能です。
ブライアン

この機能(またはアプローチ)は、Javaで行うことを実現する方法と見なすことができますか?extend非抽象サブクラスを拡張するために?私にとって、これは、internalによって実装されている既存のメソッドを使用しながら、特定のメソッドのみをオーバーライドするための便利な方法Interfaceです。
Kevin Ghaboosi 2018

それで、それは一種の継承ですか?そしてreturn r.Interface.Less(j, i)、親実装を呼び出していますか?
warvariuc

すでに2回目はこれについて混乱します。すべての答えは、この構造体の直感的でない使用法(実際に何かをソートするために必要です)を忘れているようです:sort.Sort(sort.Reverse(sort.IntSlice(example)))。私にとってここでの問題点は、Sortメソッドが逆構造体にプロモートされますが、呼び出しは非メンバー(レシーバー)スタイルです。
seebi

41

承諾した回答で理解できましたが、自分の考え方に合った説明を投稿することにしました。

「有効ゴー」が埋め込まれた他のインターフェースを有するインターフェースの例を有します。

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

および他の構造体を埋め込んだ構造体:

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

しかし、インターフェースを埋め込んだ構造体については言及されていません。私はこれをsortパッケージで見て混乱しました:

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

...

type reverse struct {
    Interface
}

しかし、アイデアは単純です。それはほとんど同じです:

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

IntSliceに昇格する方法reverse

この:

type reverse struct {
    Interface
}

つまり、sort.reverseインターフェイスを実装する構造体sort.Interfaceや、インターフェイスにあるメソッドを埋め込むことができ、それらはにプロモートされreverseます。

sort.InterfaceLess(i, j int) boolオーバーライドできるメソッドがあります:

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

理解の混乱

type reverse struct {
    Interface
}

構造体は常に固定構造、つまり固定タイプのフィールドの数が固定されていると思っていました。

しかし、以下は私が間違っていることを証明しています:

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
私の理解が正しければ、インターフェイス値は、割り当てられたインスタンスへのポインタと、インスタンスのタイプのメソッドテーブルへのポインタで表されます。したがって、すべてのインターフェイス値はメモリ内で同じ構造になります。構造的には、埋め込みは構成と同じです。したがって、インターフェースを埋め込んだ構造体でさえ、固定構造になります。インターフェイスが指すインスタンスの構造は異なります。
Nishant George Agrwal 2015

これは、はるかに詳細で、明確な例とドキュメントへのリンクを提供するため、受け入れられたものよりも優れた答えであることがわかりました。
1101001 0020

28

声明

type reverse struct {
    Interface
}

reverseインターフェイスを実装するすべてのもので初期化できますInterface。例:

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

このように、エンベディッドInterfaceバリューによって実装されたすべてのメソッドはreverse、たとえばLess並べ替えを逆にするために、それらの一部をオーバーライドできる間、外部に入力されます。

これは、を使用しsort.Reverseたときに実際に発生することです。埋め込みについては、仕様の構造体セクションで読むことができます。


すでに2回目はこれについて混乱します。すべての答えは、この構造体の直感的でない使用法(実際に何かをソートするために必要です)を忘れているようです:sort.Sort(sort.Reverse(sort.IntSlice(example)))。私にとってここでの問題点は、Sortメソッドが逆構造体にプロモートされますが、呼び出しは非メンバー(レシーバー)スタイルです。
seebi

@seebiこれが質問なら、わかりません。ごめんなさい。また、Sortメソッドはプロモートされません。満足するものが必要でsort.Interfaceあり、リバースはそのようなものです。埋め込まれたsort.Interfaceデータの関連するメソッドを変更するだけで、結果のソートがリバースされます。
nemo

質問ではなく、ただの発言です、あなたは正しいです、私はLess、Swap、Lenメソッドを意味しました!
seebi

7

私も説明します。sortパッケージは、アンエクスポートタイプ定義reverse構造体であり、その埋め込み動画をInterface

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

これにより、Reverseは別のインターフェイス実装のメソッドを使用できます。これはいわゆるcomposition、Goの強力な機能です。

Lessメソッドは、エンベディッド値のメソッドをreverse呼び出しますが、インデックスが反転しているため、並べ替え結果の順序が逆になります。LessInterface

// 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そして、Swapの他の2つの方法はreverse、暗黙のうちに元によって提供されInterface、それが埋め込まれたフィールドであるため、値。エクスポートされたReverse関数reverseは、元のInterface値を含むタイプのインスタンスを返します。

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

私にはこれは継承のように見えます。「のLessメソッドは、エンベディッドバリューのメソッドをreverse呼び出しますが、インデックスが反転しているため、並べ替え結果の順序が逆になります。」-これは親実装の呼び出しのように見えます。LessInterface
warvariuc

タイプreverseにインターフェイスインターフェイスを実装するフィールドが1つしかない限り、インターフェイスインターフェイスのメンバーにもなります:0
Allan Guwatudde

1

この機能テストでモックを作成するときに非常に役立ちます。

これがそのような例です:

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

使用することによって:

type storeMock struct {
    Store
    ...
}

すべてをあざける必要はありません Storeメソッド。HealthCheckこの方法のみがTestIsHealthyテストで使用されるため、モックのみが可能です。

結果の下に testコマンド下:

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

現実の世界の例テストするときに1を見つけることができます。このユースケースのAWS SDKを


それをさらに明確にするために、ここに醜い代替案があります-Storeインターフェースを満たすために実装する必要がある最小のもの:

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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.