You may achieve what you want if you don't try to push everything to be the plugins' responsibility.
For example if you want your plugins to be independent from each other (that is, they shouldn't know about each other) and you want all your plugins to be optional (that is, you want to choose what plugins you want to turn on), you may choose to create the wrapper type (the wrapper struct
) at the place of usage; which embeds only the plugins you want to use.
See this example, which defines a base Thing
type, and defines 3 optional plugins, all which don't know about each other, only about the Thing
type. Then let's say we want a "thing" extended with Plugin1
and Plugin3
, we can create a custom wrapper Thing13
which embeds *Plugin1
and *Plugin3
only (besides *Thing
of course).
type Thing struct{ Name string }
func (t *Thing) Stuff() { fmt.Printf("Stuff, name: %s (%p)
", t.Name, t) }
type Plugin1 struct{ *Thing }
func (p1 *Plugin1) Stuff1() { fmt.Printf("Stuff1, name: %s (%p)
", p1.Name, p1.Thing) }
type Plugin2 struct{ *Thing }
func (p2 *Plugin2) Stuff2() { fmt.Printf("Stuff2, name: %s (%p)
", p2.Name, p2.Thing) }
type Plugin3 struct{ *Thing }
func (p3 *Plugin3) Stuff3() { fmt.Printf("Stuff3, name: %s (%p)
", 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()
}
Output (try it on the Go Playground):
BaseThing
Stuff, name: BaseThing (0x1040a130)
Stuff1, name: BaseThing (0x1040a130)
Stuff3, name: BaseThing (0x1040a130)
Please note that as only a pointer to Thing
is embedded in each struct (*Thing
), there is only one Thing
value created, and it is shared across all utilized plugins (via its pointer/address), the printed pointers prove this. Also note that the Thing13
type declaration doesn't need to be in the main()
function, I just did that to save some space.
Note:
Although I implemented plugins in a way that they embed *Thing
, this is not a requirement. The *Thing
in plugins may be a "normal" field, and everything would still work as expected.
It could look like this (the rest of the code is unchanged):
type Plugin1 struct{ t *Thing }
func (p1 *Plugin1) Stuff1() { fmt.Printf("Stuff1, name: %s (%p)
", p1.t.Name, p1.t) }
type Plugin2 struct{ t *Thing }
func (p2 *Plugin2) Stuff2() { fmt.Printf("Stuff2, name: %s (%p)
", p2.t.Name, p2.t) }
type Plugin3 struct{ t *Thing }
func (p3 *Plugin3) Stuff3() { fmt.Printf("Stuff3, name: %s (%p)
", p3.t.Name, p3.t) }
Output is the same, try this variant on the Go Playground.
Note #2:
For simplicity I didn't add New()
functions for creating plugins, just used struct
literals. If creation is complex, it can be added of course. It could look like this for Plugin1
if it is in package plugin1
:
func New(t *Thing) *Plugin1 {
p := &Plugin1{t}
// Do other complex things
return p
}
And using it:
t13 := &Thing13{t, plugin1.New(t), &Plugin3{t}}
Note #3:
Also note that a new, named type is not required to acquire a "thing" value armored with Plugin1
and Plugin3
. You could just use an anonymous struct type and literal, like this:
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}}