【问题标题】:Anonymous interface implementation in GolangGolang 中的匿名接口实现
【发布时间】: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)
}

【问题讨论】:

    标签: go anonymous-types


    【解决方案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()
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-31
          • 1970-01-01
          • 1970-01-01
          • 2015-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多