嵌入结构以覆盖方法

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

Embedding structs to override methods

问题

我已经翻译好了你的内容:

我在一个名为Manager的结构体上定义了一些方法,该结构体包含一个Context。这些Manager是有版本的,允许新版本只定义已更改的函数,并在未重新定义的情况下自动使用旧版本的函数。

type Context struct { ... }

type Manager1 struct{
    Context Context
}

type Manager2 struct {
    Manager1
    Context Context
}

当在Manager2上调用一个在Manager2上未定义的函数时,Context为nil。有没有一种方法可以使上下文可用?

这个示例比我能解释得更好地演示了这个问题:
http://play.golang.org/p/gFe6GgUKEJ

英文:

TL;DR See playground link at bottom.

I have methods defined on a Manager struct which contains a Context. The Managers are versioned, allowing a new version to only define functions which have changed, and automatically use the functions from the old version if they are not redefined.

type Context struct { ... }

type Manager1 struct{
    Context Context
}

type Manager2 struct {
    Manager1
    Context Context
}

When calling a function on Manager2 which is not defined on Manager2 the Context is nil. Is there a way to do this where the context will be available?

This example demonstrates the problem better than I can explain it:
http://play.golang.org/p/gFe6GgUKEJ

答案1

得分: 3

你误用了嵌入。问题在于你在Manager1上定义了Context,尽管如此,你在每个后续类型上重新定义了它。在Manager3中,你设置了它的Context实例的值。当调用Hello()时,它被定义在Manager2上并访问它的Context实例,该实例没有值。请查看这个示例以演示:http://play.golang.org/p/XebShA9ap4

关键代码是:
m3 = Manager3{Manager2: Manager2{Context: Context{Value: "testing3"}}}

正如你所看到的,如果我实例化嵌入在Manager3中的Manager2实例并设置它的Context值,它会被打印出来。我建议你更改你的类型,使Context只在Manager1上定义,然后在初始化类型时使用我示例中的语法。

编辑:为了将评论中讨论的设计写下来,你需要将你的类型更改为以下形式:

type Context struct {
    Value string
}

type Manager1 struct {
    Context Context
}

type Manager2 struct {
    Manager1
}

type Manager3 struct {
    Manager2
}

完全删除Manager2上的Hello()实现。然后将你的复合字面量初始化更新为以下形式:

m1 := Manager1{Context: Context{Value: "testing1"}}
m2 := Manager2{Manager1: Manager1{Context: Context{Value: "testing2"}}}
m3 := Manager3{Manager2: Manager2{Manager1: Manager1{Context: Context{Value: "testing3"}}}}
英文:

You're misusing embedding. The thing is, you've defined Context on Manager1, in spite of that you've redefined it on each subsequent type. In Manager3 you're setting the value for it's Context instance. When Hello() is called, it's defined on Manager2 and accesses it's Context instance which doesn't have a value. Check out this example to demonstrate that http://play.golang.org/p/XebShA9ap4

Money line is:
m3 = Manager3{Manager2: Manager2{Context: Context{Value: "testing3"}}}

As you can see, if I instantiate the Manager2 instance embedded in Manager3 and set it's Context value it gets printed. I would recommend changing your types so that Context is only defined on Manager1 and then use syntax like that in my example when you initialize your types.

EDIT: To put the design discussed in comments in writing, you would change your types to this;

type Context struct {
    Value string
}

type Manager1 struct {
    Context Context
}

type Manager2 struct {
    Manager1
}

type Manager3 struct {
    Manager2
}

Delete the implementation of Hello() on Manager2 altogether. Then update your composite-literal initialization to this;

m1 := Manager1{Context: Context{Value: "testing1"}}
m2 := Manager2{Manager1: Manager1{Context: Context{Value: "testing2"}}}
m3 := Manager3{Manager2: Manager2{Manager1: Manager1{Context: Context{Value: "testing3"}}}}

答案2

得分: 2

你误解了通过嵌入结构体调用方法的方式:这只是语法糖!

你可以将m3.Manager2.Hello()缩写为m3.Hello(),但是Hello方法从来没有在m3上调用,而是始终在嵌入的Manager2上调用(该结构体的Contex为nil)。

嵌入不允许“覆盖方法”。嵌入不是子类化,它只是一种语法糖。

英文:

You are misunderstanding calling methods of embedded structs via the embedding struct: This is just syntactic sugar!

You are allowed to abbreviate m3.Manager2.Hello() to m3.Hello() but the Hello method is never invoked "on" m3 but always on the embedded Manager2 (which has a nil Contex).

Embedding does not allow to "overwrite methods". Embedding is not subclassing, it is syntactic sugar only.

答案3

得分: 0

您的Manager3结构包含多个Context成员 - 您正在设置Manager3.Context,但打印的是Manager2.Context。您可能想要做的是从Manager1Manager2中删除冗余的Context成员,并以以下方式初始化结构体:

var (
    m1 = Manager1{Context: Context{Value: "testing1"}}
    m2 = Manager2{Manager1: Manager1{Context: Context{Value: "testing2"}}}
    m3 = Manager3{Manager2: Manager2{Manager1: Manager1{Context: Context{Value: "testing3"}}}}
)

完整示例请参见:http://play.golang.org/p/13DOYKWN5C

提供访问器或工厂函数可能会使代码更加优雅。

英文:

Your Manager3 struct contains a number of Context members - you're setting Manager3.Context, but printing Manager2.Context. What you're probably looking for is removing the redundant Context members from Manager1 and Manager2 and initializing the structs this way:

var (
    m1 = Manager1{Context: Context{Value: "testing1"}}
    m2 = Manager2{Manager1: Manager1{Context: Context{Value: "testing2"}}}
    m3 = Manager3{Manager2: Manager2{Manager1: Manager1{Context: Context{Value: "testing3"}}}}
)

Full example here: http://play.golang.org/p/13DOYKWN5C

Providing accessors or factory functions might make this a little nicer.

huangapple
  • 本文由 发表于 2015年10月28日 05:21:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/33378714.html
匿名

发表评论

匿名网友

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

确定