Golang中的匿名接口实现

时间:2015-07-11 21:30:52

标签: go anonymous-types

在Go中,有没有办法匿名满足界面?它看起来并不像,但这是我最好的尝试。

(在Playground

package main

import "fmt"

type Thing interface {
    Item() float64
    SetItem(float64)
}

func newThing() Thing {
    item := 0.0
    return struct {
        Item (func() float64)
        SetItem (func(float64))
    }{
        Item: func() float64 { return item },
        SetItem: func(x float64) { item = x },
    }
}

func main() {
    thing := newThing()
    fmt.Println("Hello, playground")
    fmt.Println(thing)
}

4 个答案:

答案 0 :(得分:43)

Go使用method sets来声明属于某个类型的方法。使用接收器类型(方法)声明函数只有一种方法:

func (v T) methodName(...) ... { }

由于禁止嵌套函数,因此无法在匿名结构上定义方法集。

不允许这样做的第二件事是方法是只读的。引入了Method values以允许传递方法并在goroutine中使用它们但不操纵方法集。

您可以做的是提供ProtoThing并引用您的匿名结构(on play)的底层实现:

type ProtoThing struct { 
    itemMethod func() float64
    setItemMethod func(float64)
}

func (t ProtoThing) Item() float64 { return t.itemMethod() }
func (t ProtoThing) SetItem(x float64) { t.setItemMethod(x) }

// ...

t := struct { ProtoThing }{}

t.itemMethod = func() float64 { return 2.0 }
t.setItemMethod = func(x float64) { item = x }

这是有效的,因为嵌入ProtoThing方法集是继承的。因此,匿名结构也满足Thing接口。

答案 1 :(得分:11)

这是满足匿名函数接口的巧妙方法。

type Thinger interface {
    DoThing()
}

type DoThingWith func()

// Satisfy Thinger interface.
// So we can now pass an anonymous function using DoThingWith, 
// which implements Thinger.
func (thing DoThingWith) DoThing() {
    // delegate to the anonymous function
    thing()
}

type App struct {
}

func (a App) DoThing(f Thinger) {
    f.DoThing()
}


//...Somewhere else in your code:
app := App{}

// Here we use an anonymous function which satisfies the interface
// The trick here is to convert the anonymous function to the DoThingWith type
// which delegates to the anonymous function

app.DoThing(DoThingWith(func() {
    fmt.Println("Hey interface, are you satisfied?")
}))

游乐场:https://play.golang.org/p/k8_X9g2NYc

nb,看起来http包中的HandlerFunc使用这种模式:https://golang.org/pkg/net/http/#HandlerFunc

编辑:为了清楚起见,将类型DoThing更改为DoThingWith。更新了游乐场

答案 2 :(得分:1)

你不能用方法实例化一个结构,它们需要被声明为函数,但是在Go函数中是“一等公民”,所以它们可以像在JavaScript中一样是字段值(但是键入)。

您可以创建一个接受func字段的通用结构来实现接口:

package main

import "fmt"

type Thing interface {
    Item() float64
    SetItem(float64)
}

// Implements Thing interface
type thingImpl struct {
    item    func() float64
    setItem func(float64)
}
func (i thingImpl) Item() float64     { return i.item() }
func (i thingImpl) SetItem(v float64) { i.setItem(v) }

func newThing() Thing {
    item := 0.0
    return thingImpl{
        item:    func() float64 { return item },
        setItem: func(x float64) { item = x },
    }
}

func main() {
    thing := newThing()
    fmt.Println("Hello, playground")
    fmt.Println(thing)
}

答案 3 :(得分:0)

我是在研究典型的 http 中间件实现如何返回 http.Handler 接口而不为它返回的匿名函数定义 ServeHTTP 方法时来到这里的:

func LoggingMiddleware(next http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.Method, r.URL.String())
        next.ServeHTTP(w, r)
    }
    return http.HandlerFunc(fn)
}

这里将匿名函数转换为HandlerFunc类型以满足接口要求:http.HandlerFunc(fn)

即使匿名函数没有通过实现 ServeHTTP 方法直接实现 Handler interface 本身,但它转换为的 HandlerFunc type 确实实现了 ServeHTTP 方法。

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

这是一个精简的游乐场示例:https://play.golang.org/p/JX0hrcXyj6Q

package main

import (
    "fmt"
)

type Walker interface {
    Walk() // interface to be fulfilled by anonymous function
}

type WalkerFunc func()

// Walk method satisfies Walker interface
func (wf WalkerFunc) Walk() {
    fmt.Println("start walking")
    wf()
    fmt.Printf("stop walking\n\n")
}

func main() {
    // use type conversion to convert anonymous function to WalkerFunc type, satisfying Walker interface
    WalkerFunc(func() { fmt.Println("chew gum") }).Walk()
    WalkerFunc(func() { fmt.Println("smell roses") }).Walk()
}