dongnai5905 2016-06-26 22:11
浏览 66
已采纳

父结构上的Golang嵌入式接口

I've got a program that is trying to implement functions on "subclasses", where the parent can check to see if the interface is implemented. For perspective, it's really dealing with REST URL generation based on if methods exist.

What I'm running into is that based on the following pattern, both the IList and IGet interfaces are found on the TestController object, when only 1 is implemented. When the IGet interface is called I get a panic.

I would rather not make concrete definitions of the Get/List on the base struct and then have to override them, would much rather do the test for existence then go from there.

Here's a go playground link as well https://play.golang.org/p/5j58fejeJ3

package main

import "fmt"

type IGet interface {
    Get(int)
}

type IList interface {
    List(int)
}

type Application struct {
    name    string
}

type BaseAppController struct {
    *Application

    IGet
    IList
}

type TestController struct {
    *BaseAppController
}

func (ctrl *BaseAppController) Init() {
    fmt.Println("In Init")

    if f, ok := interface{}(ctrl).(IGet); ok {
        fmt.Println("Controller Found GET", f)
    } else {
        fmt.Println("Controller NOT Found GET", f)
    }

    if f, ok := interface{}(ctrl).(IList); ok {
        fmt.Println("Controller Found LIST", f)
    } else {
        fmt.Println("Controller NOT Found LIST", f)
    }
}

func (ctrl *BaseAppController) Call() {
    fmt.Println("In Call")

    if f, ok := interface{}(ctrl).(IGet); ok {
        fmt.Println("Controller Found GET - going to call", f)

        f.Get(7)
    } else {
        fmt.Println("Controller NOT Found GET - can't call", f)
    }
}

// Test controller implements the Get Method
func (ctrl *TestController) Get(v int) {
    fmt.Printf("Hi name=%s v=%d
", ctrl.name, v)
}

func main() {
    app := Application{"hithere"}
    ctrl := TestController{&BaseAppController{Application: &app}}

    ctrl.Init()

    ctrl.Call()
}
  • 写回答

4条回答 默认 最新

  • dqol6556 2016-06-27 16:00
    关注

    One thing you seem to be missing is how embedding interfaces affects a structure in Go. See, embedding promotes all of the methods of the embedded type (struct or interface, doesn't matter) to be methods of the parent type, but called using the embedded object as the receiver.

    The practical side effect of this is that embedding an interface into a structure guarantees that that structure fulfills the interface it is embedding, because it by definition has all of the methods of that interface. Trying to call any of those methods without defining something to fill that interface field in the struct, however, will panic, as that interface field defaults to nil.

    As a result, your type assertions will always be true. BaseAppController embeds both the IGet and IList interfaces, and therefore always fulfills both.

    If you want duck-typing, where behavior is selectively enabled based on the presence or absence of methods on a type, you need to use something similar to how the standard library io.WriterTo interface works. This interface, and io.ReaderFrom, are optional interfaces that io.Writer and io.Reader objects can implement to directly write to or read from another source, instead of the io package needing to buffer the read data or data to be written itself.

    The basic gist is that you define an underlying interface with the required methods on it, and that's what you pass around. You then have one or more optional interfaces, which you can check the passed type to see if they fulfill, and if so, use that optional interface's methods (and if not, revert to default behavior). Embedding isn't needed in this case.

    Embedding of interfaces, rather than being for duck-typing, is more about polymorphism. As an example, if you wanted to access a SQL database, but wanted to be able to handle both standard database calls and calls within a transaction, you could make a structure that holds the joint methods of the two types (sql.DB and sql.Tx) like this:

    type dber interface {
        Query(query string, args ...interface{}) (*sql.Rows, error)
        QueryRow(query string, args ...interface{}) *sql.Row
        Exec(query string, args ...interface{}) (sql.Result, error)
    }
    

    Then you make a structure like this:

    type DBHandle struct {
        dber
    }
    

    Now you can store either a sql.DB or a sql.Tx in that dber slot of the structure, and anything using DBHandle (as well as all methods of DBHandle itself) can call Query(), QueryRow(), and Exec() on the DBHandle without having to know whether they are being called within the scope of a transaction or not (remember, though, that interface field must be initialized first!)

    This type of functionality is where embedding really starts to shine, as it allows functionality and flexibility close to a fully polymorphic inheritance system without the need of explicit "implements" or "extends" statements. It's just not really useful for the type of dynamic duck-typing behavior you're going for.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料