ความหมายของโครงสร้างที่มีอินเทอร์เฟซที่ไม่ระบุชื่อในตัว?


89

sort แพ็คเกจ:

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

...

type reverse struct {
    Interface
}

ความหมายของอินเทอร์เฟซแบบไม่ระบุชื่อInterfaceในโครงสร้างreverseคืออะไร?


สำหรับการค้นหาที่มีคำอธิบายที่ง่ายมากที่นี่: มองใกล้ที่ golang จากมุมมองของสถาปนิก อย่าปล่อยให้ชื่อบทความทำให้คุณกลัว :)
7stud

10
AIUI บทความนั้น ("A Closer Look ... ") ไม่ได้พูดถึงความหมายของการฝังอินเทอร์เฟซที่ไม่ระบุตัวตนลงในโครงสร้าง แต่พูดถึงอินเทอร์เฟซโดยทั่วไป
Adrian Ludwin

คำตอบ:


73

ด้วยวิธีนี้จะใช้วิธีย้อนกลับsort.Interfaceและเราสามารถแทนที่วิธีการเฉพาะโดยไม่ต้องกำหนดวิธีอื่นทั้งหมด

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

สังเกตว่าที่นี่มันสลับ(j,i)แทนที่จะเป็นอย่างไร(i,j)และนี่เป็นวิธีเดียวที่ประกาศสำหรับโครงสร้างreverseแม้ว่าจะreverseใช้งาน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)
}

สิ่งที่ struct จะถูกส่งภายในวิธีนี้เราแปลงเป็นใหม่reversestruct

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

มูลค่าที่แท้จริงเกิดขึ้นหากคุณคิดว่าคุณจะต้องทำอย่างไรหากแนวทางนี้ไม่สามารถทำได้

  1. เพิ่มReverseวิธีการอื่นในsort.Interface?
  2. สร้าง ReverseInterface อื่นไหม
  3. ... ?

การเปลี่ยนแปลงใด ๆ นี้จะต้องใช้โค้ดอีกหลายบรรทัดในหลายพันแพ็กเกจที่ต้องการใช้ฟังก์ชันย้อนกลับมาตรฐาน


3
ดังนั้นจึงช่วยให้คุณสามารถกำหนดวิธีการบางอย่างของอินเทอร์เฟซใหม่ได้?
David 天宇 Wong

2
ส่วนที่สำคัญคือการที่reverseมีสมาชิกInterfaceจากประเภท จากนั้นสมาชิกนี้มีเมธอดที่เรียกได้บนโครงสร้างด้านนอกหรือแทนที่ได้
ไบรอัน

คุณลักษณะนี้ (หรือแนวทาง) ถือเป็นหนทางในการบรรลุสิ่งที่เราทำใน Java ได้หรือไม่ extendสำหรับการขยายคลาสย่อยที่ไม่ใช่นามธรรม? Interfaceให้ฉันนี้อาจเป็นวิธีที่มีประโยชน์ในการแทนที่วิธีการบางอย่างเท่านั้นในขณะที่ใช้คนที่มีอยู่ว่าจะดำเนินการโดยภายใน
Kevin Ghaboosi

มันเป็นมรดกแบบไหน? และreturn r.Interface.Less(j, i)กำลังเรียกใช้พาเรนต์หรือไม่
warvariuc

เป็นครั้งที่ 2 แล้วที่ฉันสับสนเกี่ยวกับเรื่องนี้ ทุกคำตอบดูเหมือนจะลืมการใช้งาน unintuitive ของ struct นี้ sort.Sort(sort.Reverse(sort.IntSlice(example)))(ซึ่งเป็นสิ่งจำเป็นเพื่อจัดเรียงจริงอะไร): สำหรับฉันแล้วจุดเจ็บปวดที่นี่: วิธีการเรียงลำดับได้รับการเลื่อนระดับเป็นโครงสร้างย้อนกลับ แต่การโทรไม่ใช่สไตล์สมาชิก (ผู้รับ)
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
}

วิธีการของการส่งเสริมให้IntSlicereverse

และนี่:

type reverse struct {
    Interface
}

หมายความว่าsort.reverseสามารถฝัง struct ใด ๆ ที่ดำเนินการติดต่อและสิ่งที่วิธีการที่อินเตอร์เฟซที่มีพวกเขาจะได้รับการเลื่อนตำแหน่งให้เป็นsort.Interfacereverse

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

ฉันพบว่านี่เป็นคำตอบที่ดีกว่าคำตอบที่ได้รับการยอมรับเนื่องจากให้รายละเอียดมากกว่าตัวอย่างที่ชัดเจนและลิงก์ไปยังเอกสาร
110100100

28

คำสั่ง

type reverse struct {
    Interface
}

ช่วยให้คุณสามารถเริ่มต้นกับทุกอย่างที่ดำเนินการอินเตอร์เฟซreverse Interfaceตัวอย่าง:

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

ด้วยวิธีนี้วิธีการทั้งหมดที่นำไปใช้โดยInterfaceค่าที่ฝังไว้จะได้รับการเติมข้อมูลไปยังภายนอกในขณะที่คุณยังคงสามารถแทนที่บางส่วนได้reverseตัวอย่างเช่นLessเพื่อย้อนกลับการเรียงลำดับ

sort.Reverseนี่คือสิ่งที่เกิดขึ้นจริงเมื่อคุณใช้ คุณสามารถอ่านเกี่ยวกับการฝังในส่วนโครงสร้างของข้อมูลจำเพาะ


เป็นครั้งที่ 2 แล้วที่ฉันสับสนเกี่ยวกับเรื่องนี้ ทุกคำตอบดูเหมือนจะลืมการใช้งาน unintuitive ของ struct นี้ sort.Sort(sort.Reverse(sort.IntSlice(example)))(ซึ่งเป็นสิ่งจำเป็นเพื่อจัดเรียงจริงอะไร): สำหรับฉันแล้วจุดเจ็บปวดที่นี่: วิธีการเรียงลำดับได้รับการเลื่อนระดับเป็นโครงสร้างย้อนกลับ แต่การโทรไม่ใช่สไตล์สมาชิก (ผู้รับ)
seebi

@seebi หากเป็นคำถามที่ฉันไม่เข้าใจขออภัย นอกจากนี้Sortวิธีการไม่ได้รับการส่งเสริมต้องใช้บางสิ่งที่ตอบสนองsort.Interfaceและย้อนกลับเป็นสิ่งนั้นเพียงแค่เปลี่ยนวิธีการที่เกี่ยวข้องของsort.Interfaceข้อมูลที่ฝังไว้เพื่อให้การเรียงลำดับผลลัพธ์กลับ
ฟาย

ไม่ใช่คำถามแค่คำพูดคุณพูดถูกฉันหมายถึงวิธี Less, Swap, Len!
Seebi

7

ฉันจะให้คำอธิบายของฉันด้วย sortแพคเกจที่กำหนดประเภท unexported reverseซึ่งเป็น struct, Interfaceฝังว่า

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

สิ่งนี้อนุญาตให้ Reverse ใช้วิธีการของการใช้งานอินเทอร์เฟซอื่น นี่คือสิ่งที่เรียกว่าcompositionซึ่งเป็นคุณสมบัติที่มีประสิทธิภาพของ Go

LessวิธีการreverseโทรLessวิธีการฝังตัว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)
}

LenและSwapอีกสองวิธีการreverseระบุโดยปริยายโดยInterfaceค่าดั้งเดิมเนื่องจากเป็นฟิลด์ฝังตัว Reverseฟังก์ชันที่ส่งออกจะส่งคืนอินสแตนซ์ของreverseชนิดที่มีInterfaceค่าดั้งเดิม

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

สำหรับฉันสิ่งนี้ดูเหมือนมรดก " LessวิธีการreverseเรียกLessเมธอดของInterfaceค่าฝังตัวแต่เมื่อดัชนีพลิกกลับลำดับของผลลัพธ์การจัดเรียง" - ดูเหมือนการเรียกใช้งานหลัก
warvariuc

ตราบเท่าที่ type reverse มีเพียงฟิลด์เดียวที่ใช้อินเทอร์เฟซอินเทอร์เฟซมันยังกลายเป็นสมาชิกของอินเทอร์เฟซอินเทอร์เฟซ: 0
Allan Guwatudde

1

ผมพบว่าคุณลักษณะนี้มีประโยชน์มากเมื่อเขียนmocksในการทดสอบ

นี่คือตัวอย่าง:

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.