Golang 中的匿名接口实现

Posted

技术标签:

【中文标题】Golang 中的匿名接口实现【英文标题】:Anonymous interface implementation in Golang 【发布时间】:2015-09-30 11:00:32 【问题描述】:

在 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)

【问题讨论】:

【参考方案1】:

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

func (v T) methodName(...) ...  

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

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

您可以做的是提供一个 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接口。

【讨论】:

这真是太棒了,我喜欢它是半结构化的。嵌入真的很整洁。 起初我读过“不可能”的部分,但后来回来并实际运行了它!不错! @nemo 为什么 ProtoThing 是一个结构,而不是一个接口? @EdgarAroutiounian 问题是关于 Go 中的一个构造,它可以匿名满足接口(在本例中为 Thing),即不声明命名类型。 ProtoThing 是一个构造,它允许您满足 Thing 接口但交换方法的功能。 此时ProtoThing感觉和OOP中的抽象类一样【参考方案2】:

这是用匿名函数满足接口的好方法。

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。更新游乐场

【讨论】:

【参考方案3】:

你不能用方法来实例化结构,它们需要被声明为函数,但在 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)

【讨论】:

【参考方案4】:

当我研究典型的 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()

【讨论】:

以上是关于Golang 中的匿名接口实现的主要内容,如果未能解决你的问题,请参考以下文章

golang结构体组合与“多态” 2021-08-06

TypeScript 中的匿名/内联接口实现

[Golang]面向对象营养餐,一文管够(封装,继承,多态)

如何实现带返回类型的接口方法是Golang中的一个接口

Golang basic_leaming接口

Golang basic_leaming接口