[go] 익명 인터페이스가 포함 된 구조체의 의미?

sort 꾸러미:

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

...

type reverse struct {
    Interface
}

Interfacestruct에서 익명 인터페이스의 의미는 무엇입니까 reverse?



답변

이런 식으로 reverse는를 구현 sort.Interface하고 다른 모든 메소드를 정의하지 않고도 특정 메소드를 재정의 할 수 있습니다.

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

여기에서 어떻게 (j,i)대신 스왑되는지 주목하십시오. (i,j)또한 이것은 구현 reverse하더라도 구조체에 대해 선언 된 유일한 메서드입니다.reversesort.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. …?

이러한 변경에는 표준 역방향 기능을 사용하려는 수천 개의 패키지에 걸쳐 더 많은 코드 줄이 필요합니다.


답변

좋아, 받아 들인 대답은 이해하는 데 도움이되었지만 내 사고 방식에 더 적합한 설명을 게시하기로 결정했습니다.

“효과적인 이동” 임베디드 다른 인터페이스를 가진 인터페이스의 예를 가지고 :

// 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.Interface방법이 Less(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"})
}


답변

진술

type reverse struct {
    Interface
}

reverse인터페이스를 구현하는 모든 항목 으로 초기화 할 수 있습니다 Interface. 예:

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

이렇게하면 포함 된 Interface값에 의해 구현 된 모든 메서드 가 외부로 채워지면서 에서 일부 메서드 를 재정의 할 수 있습니다 ( reverse예 : Less정렬을 반대로).

를 사용할 때 실제로 일어나는 일 sort.Reverse입니다. spec의 struct 섹션에서 임베딩 대해 읽을 수 있습니다 .


답변

저도 설명하겠습니다. sort패키지는 안 export 형을 정의 reverse구조체, 그 퍼가기이다, Interface.

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

이를 통해 Reverse는 다른 인터페이스 구현의 메소드를 사용할 수 있습니다. 이것이 바로 compositionGo의 강력한 기능입니다.

에 대한 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)
}

LenSwap의 다른 두 메서드 는 포함 된 필드이므로 reverse원래 Interface값에 의해 암시 적으로 제공됩니다 . 내 보낸 Reverse함수는 reverse원래 Interface값 이 포함 된 유형 의 인스턴스를 반환합니다 .

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


답변

이 기능 은 테스트 에서 모의 를 작성할 때 매우 유용 합니다 .

다음은 그러한 예입니다.

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

이 사용 사례 의 실제 예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
}


답변