如何任意地扩展“对象"? [英] How to arbitrarily extend an "object"

查看:84
本文介绍了如何任意地扩展“对象"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望我的问题可以弄清楚.我已尽力做到这一点,但如果有必要请澄清.

I hope my question can be made clear. I've done my best to make this concise, but please ask for clarification if it is necessary.

在JavaScript中,通常的做法是让插件"通过创建新方法来修改现有对象.例如, jQuery插件.

In JavaScript, it's common practice to have a "plugin" modify an existing object by creating new methods. jQuery plugins do this, for example.

我需要在Go中做类似的事情,并且正在寻找实现它的最佳方法.

I have the need to do something similar in Go, and am looking for the best way to do it.

最简单的实现方法是将函数存储在map[string]func类型的数据结构中,然后使用类似以下的命令来调用这些新的方法":

The simplest to implement would simply be to store functions in a map[string]func type of data structure, then call these new "methods" with something like:

func (f *Foo) Call(name string) {
    fn := f.FuncMap[name]
    fn()
}

如果我使用接口嵌入,也可以获得更友好的API,例如:

I can also get a much friendlier API if I use interface embedding, such as:

package thingie

type Thingie struct { ... }
type Thingier interface { ... }

func New() *Thingie { ... }
func (t *Thingie) Stuff() { ... }

还有

package pluginone

type PluginOne struct { thingie.Thingier, ... }

func New(t *thingie.Thingie) *PluginOne { ... }
func (p1 *PluginOne) MoreStuff() { ... }

这有效,最多带有一个插件".也就是说,我可以创建一个对象,该对象可以访问thingiepluginone包中的所有方法.

This works, with up to one "plugin". That is to say, I can create an object which can access all the methods in both the thingie and pluginone packages.

package main

func main() {
    t := thingie.New()
    p1 := pluginone.New(t)
    p1.Stuff()
    p1.MoreStuff()
}

当我添加第二个插件时出现问题:

The problem comes when I add a second plugin:

t := thingie.New()
p1 := pluginone.New(t)
p2 := plugintwo.New(p2)
p2.Stuff() // This works
p2.MoreStuff() // P2 doesn't know about pluginone's methods, so this fails

所以我似乎只剩下基于map[string]func的丑陋API的选项,或者最多只有一个插件".

So I seem to be left with the options of an ugly API based on map[string]func, or a maximum of a single "plugin".

我还没有考虑过其他选择吗?

Are there any other alternatives I haven't considered?

推荐答案

如果您不尝试将所有事情都归咎于插件的责任,那么您可能会实现自己想要的.

You may achieve what you want if you don't try to push everything to be the plugins' responsibility.

例如,如果您希望您的插件彼此独立(即,他们不应该彼此了解),并且希望所有插件都是可选的(即,您想选择想要的插件)打开),您可以选择在使用位置创建包装器类型(包装器struct);仅嵌入您要使用的插件.

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.

请参阅此示例,该示例定义了基本的Thing类型,并定义了3个可选的插件,所有这些插件彼此都不了解,仅关于Thing类型.然后,假设我们想要用Plugin1Plugin3扩展的东西",我们可以创建一个自定义包装器Thing13,该包装器仅嵌入*Plugin1*Plugin3(当然,除了*Thing之外).

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)\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()
}

输出(在游乐场上尝试):

BaseThing
Stuff, name: BaseThing (0x1040a130)
Stuff1, name: BaseThing (0x1040a130)
Stuff3, name: BaseThing (0x1040a130)

请注意,由于每个结构体(*Thing)中仅嵌入了指向Thing的指针,因此仅创建了一个Thing值,并且该值在所有使用的插件之间共享(通过其指针/地址) ,打印的指针证明了这一点.另外请注意,Thing13类型声明不需要在main()函数中,我只是这样做以节省一些空间.

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.

注意:

尽管我以嵌入*Thing的方式实现了插件,但这不是必需的.插件中的*Thing可能是一个正常"字段,并且一切仍将按预期工作.

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)\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) }

输出是相同的,请在游乐场上尝试这种变体.

Output is the same, try this variant on the Go Playground.

注释2:

为简单起见,我没有添加用于创建插件的New()函数,只是使用了struct文字.如果创建过程很复杂,那么当然可以添加它.如果Plugin1位于软件包plugin1中,则可能看起来像这样:

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
}

并使用它:

t13 := &Thing13{t, plugin1.New(t), &Plugin3{t}}

注释#3:

还请注意,不需要新的命名类型来获取具有Plugin1Plugin3的物"值.您可以只使用匿名结构类型和文字,例如:

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}}

这篇关于如何任意地扩展“对象"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆