Go中的即插即用包?

huangapple go评论73阅读模式
英文:

Drop-in packages in Go?

问题

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

以下是一个代码示例,可以说明我想要做的事情。

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!"
    })
}

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

src/no/no.go:

package no

import (
    "reg"
)

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

但是,似乎并不是这样工作的。

我可能只是过于用Python的思维方式来思考这个问题,但是否有一种方法可以实现类似我所期望的功能?如果没有,我将改变我的方法,并且我将学到关于Go的做事方式的新知识。

提前感谢!

英文:

How would I go about having a package register some object (for instance a function) to a registry at load time such that adding a new package to the program will automatically add new functionality to the program without having to modify code in other packages?

Here's a code sample which should illustrate what I'm trying to do.

src/say/say.go:

package main

import (
    &quot;os&quot;
    &quot;reg&quot;
)

func main() {
    if len(os.Args) != 2 {
        os.Stderr.WriteString(&quot;usage:\n    say &lt;what_to_say&gt;\n&quot;)
        os.Exit(1)
    }

    cmd, ok := reg.GetFunc(os.Args[1])
    if ok {
        os.Stdout.WriteString(cmd())
        os.Stdout.Write([]byte{&#39;\n&#39;})
    } else {
        os.Stderr.WriteString(&quot;I can&#39;t say that!\n&quot;)
        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 (
    &quot;reg&quot;
}

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

When coding this up, I naively supposed that perhaps the package "hi" would be found by the go compiler and compiled into the binary. Then, at load time, the init() function would run. If that was how things worked, I'd have been able to drop in something like the following to add a new "say no" command:

src/no/no.go:

package no

import (
    &quot;reg&quot;
)

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

But, it doesn't seem to work that way.

I may just be thinking about the problem too much through a Pythonic lens, but is there some way to accomplish something somewhat like what I'm shooting for? If not, I'll change my tack and I will have learned something new about the Go way of doing things.

Thanks in advance!

答案1

得分: 3

package main

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

And previous package no

// Command no
package cmds

import (
"reg"
)

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

英文:

Since you must use import in order for the compiler add a package, my suggestion would be to do the following:

Instead of using multiple drop-in packages, you could have only one single package with multiple drop-in files. Each command file is placed in the same package folder (cmds). This is possible since you are allowed to have multiple init in a package, and you would not have to make any edits to say.go, no matter how many new drop-in files you add.

package main

import (
    &quot;os&quot;
    &quot;reg&quot;
    _ &quot;cmds&quot;
)
....

And previous package no

// Command no
package cmds

import (
    &quot;reg&quot;
)

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

答案2

得分: 2

根据我对init函数的阅读,我认为如果你只是在say.go中将"hi"和"no"添加到你导入的包列表中,你的示例应该可以工作。如果你这样做,它能工作吗?

我知道你不想改变say.go中的代码,所以我想这并不是一个真正的解决方案。

英文:

Based on what I read about the init function, I think your example would work if you just added "hi" and "no" to the list of packages you are importing in say.go. Does it work if you do that?

I know you did not want to change the code in say.go, so I suppose that isn't really a solution.

huangapple
  • 本文由 发表于 2012年9月2日 04:37:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/12231196.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定