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

时间:2016-09-13 16:34:29

标签: go

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

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

我尝试了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 个答案:

答案 0 :(得分: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 :(得分: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)
}

答案 2 :(得分: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_