如果你不试图把一切都推到插件的责任上,你可能会实现你想要的。
例如,如果您希望您的插件彼此独立(也就是说,它们不应该相互了解)并且您希望所有插件都是可选的(也就是说,您想选择您想要的插件开启),您可以选择在使用的地方创建包装器类型(包装器struct);仅嵌入您要使用的插件。
参见这个例子,它定义了一个基本的Thing 类型,并定义了3 个可选插件,它们彼此不知道,只知道Thing 类型。然后假设我们想要一个用Plugin1 和Plugin3 扩展的“事物”,我们可以创建一个自定义包装器Thing13,它只嵌入*Plugin1 和*Plugin3(当然除了*Thing)。
type Thing struct{ Name string }
func (t *Thing) Stuff() { fmt.Printf("Stuff, name: %s (%p)\n", t.Name, t) }
type Plugin1 struct{ *Thing }
func (p1 *Plugin1) Stuff1() { fmt.Printf("Stuff1, name: %s (%p)\n", p1.Name, p1.Thing) }
type Plugin2 struct{ *Thing }
func (p2 *Plugin2) Stuff2() { fmt.Printf("Stuff2, name: %s (%p)\n", p2.Name, p2.Thing) }
type Plugin3 struct{ *Thing }
func (p3 *Plugin3) Stuff3() { fmt.Printf("Stuff3, name: %s (%p)\n", p3.Name, p3.Thing) }
func main() {
t := &Thing{"BaseThing"}
// Let's say you now want a "Thing" extended with Plugin1 and Plugin3:
type Thing13 struct {
*Thing
*Plugin1
*Plugin3
}
t13 := &Thing13{t, &Plugin1{t}, &Plugin3{t}}
fmt.Println(t13.Name)
t13.Stuff()
t13.Stuff1()
t13.Stuff3()
}
输出(在Go Playground 上试试):
BaseThing
Stuff, name: BaseThing (0x1040a130)
Stuff1, name: BaseThing (0x1040a130)
Stuff3, name: BaseThing (0x1040a130)
请注意,由于每个结构 (*Thing) 中仅嵌入了一个指向 Thing 的指针,因此只创建了一个 Thing 值,并且它在所有使用的插件之间共享(通过其指针/地址) ,打印的指针证明了这一点。还要注意Thing13 类型声明不需要在main() 函数中,我这样做只是为了节省一些空间。
注意:
虽然我以嵌入*Thing 的方式实现插件,但这不是必需的。插件中的*Thing 可能是一个“正常”字段,一切仍将按预期工作。
它可能看起来像这样(其余代码不变):
type Plugin1 struct{ t *Thing }
func (p1 *Plugin1) Stuff1() { fmt.Printf("Stuff1, name: %s (%p)\n", p1.t.Name, p1.t) }
type Plugin2 struct{ t *Thing }
func (p2 *Plugin2) Stuff2() { fmt.Printf("Stuff2, name: %s (%p)\n", p2.t.Name, p2.t) }
type Plugin3 struct{ t *Thing }
func (p3 *Plugin3) Stuff3() { fmt.Printf("Stuff3, name: %s (%p)\n", p3.t.Name, p3.t) }
输出是一样的,在Go Playground上试试这个变种。
注意 #2:
为简单起见,我没有添加 New() 函数来创建插件,只是使用了 struct 文字。如果创建复杂,当然可以添加。如果 Plugin1 在包 plugin1 中,它可能看起来像这样:
func New(t *Thing) *Plugin1 {
p := &Plugin1{t}
// Do other complex things
return p
}
并使用它:
t13 := &Thing13{t, plugin1.New(t), &Plugin3{t}}
注意事项 #3:
还请注意,不需要新的命名类型来获取带有Plugin1 和Plugin3 的“事物”值。您可以只使用匿名结构类型和文字,如下所示:
t := &Thing{"BaseThing"}
// Let's say you now want a "Thing" extended with Plugin1 and Plugin3:
t13 := struct { *Thing; *Plugin1; *Plugin3 }{t, &Plugin1{t}, &Plugin3{t}}