sort
包裹:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
struct Interface
中匿名接口reverse
的含义是什么?
答案 0 :(得分:55)
以这种方式反向实现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
< / p>
// 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}
}
如果您认为如果不可能采取这种方法,您将需要做什么,真正的价值就来了。
Reverse
方法添加到sort.Interface
?任何此类更改都需要在数千个要使用标准反向功能的软件包中提供更多代码。
答案 1 :(得分:24)
好的,接受的答案帮助我理解,但我决定发布一个我觉得更适合我的思路的解释。
"Effective Go"有嵌入其他接口的接口示例:
// 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"})
}
答案 2 :(得分:18)
声明
type reverse struct {
Interface
}
允许您使用实现接口reverse
的所有内容初始化Interface
。例如:
&reverse{sort.Intslice([]int{1,2,3})}
这样,嵌入式Interface
值实现的所有方法都会填充到外部,而您仍然可以在reverse
中覆盖其中一些,例如Less
来反转排序
这是您使用sort.Reverse
时实际发生的情况。您可以阅读有关嵌入in the struct section of the spec的信息。
答案 3 :(得分:3)
我也会给出我的解释。 sort
包定义了一个未导出的类型reverse
,它是一个嵌入Interface
的结构。
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
这允许Reverse使用另一个Interface实现的方法。这就是所谓的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}
}
答案 4 :(得分: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
}
答案 5 :(得分:0)
我会尝试另一种低级别的方法来解决这个问题。 鉴于反向结构:
type reverse struct {
Interface
}
这意味着,反向结构有一个字段 reverse.Interface
,作为结构字段,它可以是 nil 或具有接口类型的值。
如果它不是 nil,则来自接口的字段被提升为“root”=反向结构。它可能会被直接在反向结构上定义的字段黯然失色,但我们的情况并非如此。
当您执行以下操作时: foo := reverse{},你可以通过 fmt.Printf("%+v", foo) 打印它并得到
{Interface:<nil>}
当你这样做
foo := reverse{someInterfaceInstance}
相当于:
foo := reverse{Interface: someInterfaceInstance}
在我看来,您声明期望,接口 API 的实现应该在运行时注入到您的结构 reverse 中。然后这个 api 将被提升到 struct reverse 的根。 同时,这仍然允许不一致,其中你有反向结构实例和 reverse.Interface = < Nil>,你编译它并在运行时得到恐慌。
当我们回顾 OP 中反向的具体示例时,我可以将其视为一种模式,即如何在运行时替换/扩展某些实例/实现类型的行为,而不是使用更像在编译中的类型嵌入结构而不是接口的时间。
不过,这让我很困惑。尤其是接口为 Nil 的状态 :(.