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


87

sort 꾸러미:

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

...

type reverse struct {
    Interface
}

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


검색 자를 위해 여기에 훨씬 더 간단한 설명이 있습니다. 건축가의 관점에서 Golang 자세히 살펴보기 . 기사 제목이 당신을 놀라게하지 마십시오. :)
7stud

10
AIUI, 그 기사 ( "A Closer Look ...")는 실제로 익명 인터페이스를 구조체에 포함하는 것이 무엇을 의미하는지에 대해 설명하지 않고 일반적인 인터페이스에 대해서만 설명합니다.
Adrian Ludwin 2018

답변:


67

이런 식으로 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. ...?

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


2
인터페이스의 일부 메소드 만 재정의 할 수 있습니까?
David 天宇 Wong

1
중요한 부분은 유형 reverse멤버 가 있다는 것 Interface입니다. 이 멤버는 외부 구조체에서 호출 가능하거나 재정의 할 수있는 메서드를 갖습니다.
Bryan

이 기능 (또는 접근 방식)을 Java에서 수행하는 작업을 수행하는 방법으로 고려할 수 있습니까? extend비추 상 하위 클래스를 확장하기 위해? 나에게 이것은 internal에 의해 구현 된 기존 메서드를 사용하는 동안 특정 메서드 만 재정의하는 편리한 방법이 될 수 있습니다 Interface.
Kevin Ghaboosi

그래서 그것은 일종의 상속입니까? 그리고 return r.Interface.Less(j, i)부모 구현을 호출하고 있습니까?
warvariuc

39

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

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

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

3
내 이해가 정확하다면 인터페이스 값은 할당 된 인스턴스에 대한 포인터와 인스턴스 유형의 메서드 테이블에 대한 포인터로 표시됩니다. 따라서 모든 인터페이스 값은 메모리에서 동일한 구조를 갖습니다. 구조적으로 임베딩은 구성과 동일합니다. 따라서 인터페이스를 포함하는 구조체조차도 고정 된 구조를 갖습니다. 인터페이스가 가리키는 인스턴스의 구조는 다릅니다.
Nishant George Agrwal 2015

훨씬 더 자세한 정보, 명확한 예 및 문서에 대한 링크를 제공했기 때문에 수락 된 답변보다 더 나은 답변을 찾았습니다.
110100100

25

진술

type reverse struct {
    Interface
}

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

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

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

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


5

저도 설명하겠습니다. 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}
}

나에게 이것은 상속처럼 보입니다. "이 Less메서드 는 포함 된 값 의 메서드를 reverse호출 하지만 인덱스가 뒤집혀서 정렬 결과의 순서가 반전됩니다." -이것은 부모 구현의 호출처럼 보입니다. LessInterface
warvariuc

리버스 타입에 인터페이스 인터페이스를 구현하는 필드가 하나만있는 한 인터페이스 인터페이스의 멤버가됩니다. 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

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