1

我将如何让包在加载时将一些对象(例如函数)注册到注册表,以便向程序添加新包将自动向程序添加新功能,而无需修改其他包中的代码?

这是一个代码示例,应该说明我正在尝试做的事情。

src/say/say.go:

package main

import (
    "os"
    "reg"
)

func main() {
    if len(os.Args) != 2 {
        os.Stderr.WriteString("usage:\n    say <what_to_say>\n")
        os.Exit(1)
    }

    cmd, ok := reg.GetFunc(os.Args[1])
    if ok {
        os.Stdout.WriteString(cmd())
        os.Stdout.Write([]byte{'\n'})
    } else {
        os.Stderr.WriteString("I can't say that!\n")
        os.Exit(1)
    }
}

src/reg/reg.go:

package reg

var registry = make(map[string]func() string)

func Register(name string, f func() string) {
    registry[name] = f
}

func GetFunc(name string) (func() string, bool) {
    f, ok := registry[name]
    return f, ok
}

src/hi/hi.go:

package hi

import (
    "reg"
}

func init() {
    reg.Register("hi", func() string {
        return "Hello there!"
    })
}

在编写代码时,我天真地认为 go 编译器可能会找到包“hi”并编译成二进制文件。然后,在加载时,init() 函数将运行。如果事情是这样的,我就可以加入如下内容来添加一个新的“say no”命令:

src/no/no.go:

package no

import (
    "reg"
)

func init() {
    reg.Register("no", func() string {
        return "Not a chance, bub."
    })
}

但是,它似乎不是那样工作的。

我可能只是通过 Pythonic 的镜头来思考这个问题太多,但是有什么方法可以完成一些有点像我正在拍摄的事情吗?如果没有,我会改变我的策略,我会学到一些关于 Go 做事方式的新知识。

提前致谢!

4

2 回答 2

3

由于您必须使用import以便编译器添加一个包,我的建议是执行以下操作:

您可以只使用一个包含多个插入文件的单个包,而不是使用多个插入。每个命令文件都放在同一个包文件夹 (cmds) 中。这是可能的,因为您可以在一个包中包含多个,并且无论您添加多少新的插入文件,您都不必对 进行任何编辑。initsay.go

package main

import (
    "os"
    "reg"
    _ "cmds"
)
....

和以前的包没有

// Command no
package cmds

import (
    "reg"
)

func init() {
    reg.Register("no", func() string {
        return "Not a chance, bub."
    })
}
于 2012-09-03T06:40:46.657 回答
2

根据我读到的有关init 函数的内容,我认为如果您只是将“hi”和“no”添加到要导入的包列表中,那么您的示例将起作用say.go。如果你这样做,它会起作用吗?

我知道您不想更改 中的代码say.go,所以我认为这并不是真正的解决方案。

于 2012-09-01T20:49:57.010 回答