我尝试制作几种类型,可以调用相同的函数来执行一些常见操作,而无需为每种类型复制它们。让我们调用那些类型的处理程序。
我的想法是我可以拥有一个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
字段。我不能在那里输入断言,因为它意味着列出可以去那里的所有类型,并且可能有很多。
所以这是我的问题:你如何编写共享代码,可以设置字段和调用类似类型的函数?
答案 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)
}
答案 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_