首页 文章

编写一个可以操作几个相似类型的函数

提问于
浏览
1

我正在尝试制作几种类型,可以调用相同的函数来执行一些常见操作,而无需为每种类型复制它们 . 我们称这些类型的处理程序 .

我的想法是我可以有一个CreateHandler,一个ListHandler等,以及一个将为这些处理程序执行默认操作的函数,比方说,将“Success”字段设置为true并在处理程序上调用“Execute”函数 . 我保持这些示例简单易读,但在实际情况下会有更常见的操作方式 .

我尝试了3种不同的方法,无济于事:使用基类型并嵌入它,使用类型实现的接口并使用空接口作为参数 .

嵌入的基本类型

package main

import "fmt"

type BaseHandler struct {
  Success bool
}

func ( handler *BaseHandler ) Init() {
  handler.Success = true
  handler.Execute()
}

func ( handler *BaseHandler ) Execute() {
}



// I will have many different types doing the same kind of work,
// with their own specific fields and functions
type MyHandler struct {
  BaseHandler
  Message string
}

func ( handler *MyHandler ) Execute() {
  fmt.Println( "I'm never called" )
}

func main() {
  handler := MyHandler{}
  handler.Init()
}

这不起作用,因为调用的 Execute 函数是来自 BaseHandler 的函数,而不是 MyHandler . 如果 Init 方法没有接收器并且将 *BaseHandler 作为参数,则会出现同样的问题 .

接口由类型实现

package main

import "fmt"

type BaseHandler interface {
  Execute()
}

func Init( handler BaseHandler ) {
  handler.Success = true
  handler.Execute()
}

type MyHandler struct {
  Success bool
  Message string
}

func ( handler *MyHandler ) Execute() {
  fmt.Println( "I'm never called" )
}

func main() {
  handler := MyHandler{}
  Init( handler )
}

由于 Init 函数的第一行,这将无法编译,说 BaseHandler 接口没有 Success 字段 .

空接口作为参数

package main

import "fmt"

func Init( handler interface{} ) {
  handler.Success = true
  handler.Execute()
}


type MyHandler struct {
  Success bool
  Message string
}

func ( handler *MyHandler ) Execute() {
  fmt.Println( "I'm never called" )
}

func main() {
  handler := MyHandler{}
  Init( handler )
}

这将无法在 Init 的第一行编译,说 interface{} 没有 Success 字段 . 我不能在那里输入断言,因为它意味着列出可以去那里的所有类型,并且可能有很多 .

问题

所以这是我的问题:你如何编写共享代码,可以设置字段和从类似类型调用函数?

3 回答

  • 1

    handler.Success = true 感觉它属于 Execute . 因此,您可以定义一个接口并在此接口上创建 Init 函数,同时在"children"中嵌入接口:

    type Handler interface {
        Execute()
    }
    
    func Init(handler Handler) {
        handler.Execute()
    }
    
    type BaseHandler struct {
        Success bool
    }
    
    func (handler *BaseHandler) Execute() {
        handler.Success = true
        fmt.Println("success: true")
    }
    
    type MyHandler struct {
        Handler
        Message string
    }
    
    func (handler *MyHandler) Execute() {
        fmt.Println("I do some work before")
        handler.Handler.Execute()
        fmt.Println("I do some work after")
    }
    
    func main() {
        handler := &MyHandler{&BaseHandler{}, "foo"}
        Init(handler)
    }
    

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

  • 1

    好像你需要init才能设置成功并执行某些操作 .

    TL; DR Heres the playground link https://play.golang.org/p/jflmT-LpJy

    因此,使用接口,我将创建以下内容

    type SuccessableExecutable interface {
        SetSuccess()
        Execute()
    }
    
    func Init(se SuccessableExecutable) {
        se.SetSuccess()
        se.Execute()
    }
    

    现在,您希望SetSuccess可以在多种类型中重复使用 . 为此,我将创建一个可嵌入所有类型的结构

    type Successable struct {
        success bool
    }
    
    func (s *Successable) SetSuccess() {
        s.success = True
    }
    

    自动嵌入这个使您可以访问 SetSuccess ,因此任何具有 Execute 并嵌入此结构的结构也将是 SuccessableExecutable .

    把它放在一起,我们有

    package main
    
    import (
        "fmt"
    )
    
    type Successable struct {
        success bool
    }
    
    func (s *Successable) SetSuccess() {
        s.success = true
    }
    
    type SuccessableExecutable interface {
        SetSuccess()
        Execute()
    }
    
    type BaseHandler struct {
        Message string
        Successable
    }
    
    func (b *BaseHandler) Execute() {
        fmt.Printf("%s\n", b.Message);
    }
    
    func Init(se SuccessableExecutable) {
        se.SetSuccess()
        se.Execute()
    }
    
    func main() {
            bh := &BaseHandler{ Message: "hello" }
            fmt.Printf("%+v\n", bh)
            Init(bh)
            fmt.Printf("%+v\n", bh)
    }
    
  • 1

    尝试嵌入接口:

    package main
    
    import "fmt"
    
    type BaseHandler struct {
        Success  bool
        Executer // embedded, so BaseHandler inherits the method
    }
    
    type Executer interface {
        Execute()
    }
    
    func (h *BaseHandler) Init() {
        h.Success = true
        h.Execute()
    }
    
    // I will have many different types doing the same kind of work,
    // with their own specific fields and functions
    type StringHandler struct {
        Message string
    }
    
    func (h *StringHandler) Execute() {
        fmt.Println(h.Message)
    }
    
    type IntHandler struct {
        Value int
    }
    
    func (h *IntHandler) Execute() {
        fmt.Println(h.Value)
    }
    
    func main() {
        stringHandler := BaseHandler{Executer: &StringHandler{"Hello"}}
        intHandler := BaseHandler{Executer: &IntHandler{42}}
        DoInit(stringHandler)
        DoInit(intHandler)
    }
    
    // These are just to prove you can pass it around as the struct
    func DoInit(h BaseHandler) {
        h.Init()
    }
    

    https://play.golang.org/p/aGZMnuoBh_

    这样就可以在基本处理程序中跟踪Success,但是Execute的实际实现可以通过任何类型动态填充(只要它满足该接口) .

    就个人而言,我喜欢再向前迈进一步,并将interface-embedding-struct作为另一个接口传递(与嵌入式相同,因为结构会自动实现它嵌入的所有接口,或者作为另一个接口与其他或其他方法一起使用) ) .

    另外,一个相关的注意事项:我总是建议将指针传递给接口,这是一个有趣的Go怪癖,存储指针的接口可以访问该存储类型的指针和值接收器方法,但是存储值的接口直接(即使接口在大多数情况下仍然存储指向该值的指针)只能访问值接收器方法 . 例如:https://play.golang.org/p/6qOYhol_y_

相关问题