覆盖嵌入结构的行为

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

Overwriting behavior of embedded struct

问题

我有一个类型为XY的结构体,它有各种字段和方法(几十个)。

type XY struct {
    Name      string
    SomeValue int
    ...
}

func (xy *XY) Do1() { ... }
func (xy *XY) Do2() { ... }
func (xy *XY) Do3() { ... }
...

现在我想定义一个第二个类型,它嵌入了XY,并保留所有的字段和方法。但是我确实想修改一些函数。

type AB struct {
    XY
}

func (ab *AB) Do2() { ... }

到目前为止都还好。现在我想将AB传递给一个接受XY类型参数的函数。

func SomeFunc(xy *XY) { ... }

这就是我遇到问题的地方,没有多态性。

我可以将*AB.XY传递给函数,但这将不再使用AB的Do2函数。我可以为此创建一个接口,这可能是预期的方式,但是如果SomeFunc需要几乎所有XY的函数,比如几乎所有字段的getter,我基本上需要创建一个XY的副本作为接口(可能的用例是我在服务器上,并且必须以特定方式向客户端发送值)。我不想只将其作为接口,因为我将不得不在使用接口的所有类型中重新定义所有字段和函数。

我可以想到一些解决这个问题的方法,例如将Do2作为闭包,根据“宿主”类型进行设置,或者在XY中使用枚举字段,并根据该“类型”变量更改Do2的行为,或者在SomeFunc中使用反射和interface{}。但我想知道在Go中的“正确”方式是什么。无论你有多少函数,你如何以最高效的方式做到这一点?

英文:

I have a type XY that has various fields and methods (a few dozen).

type XY struct {
    Name      string
    SomeValue int
    ...
}

func (xy *XY) Do1() { ... }
func (xy *XY) Do2() { ... }
func (xy *XY) Do3() { ... }
...

Now I want to define a second type that embeds XY, keeping all fields and methods. But I do want to modify a few functions.

type AB struct {
    XY
}

func (ab *AB) Do2() { ... }

So far so good. Now I want to pass AB to a function that takes XY.

func SomeFunc(xy *XY) { ... }

And here's where I stumble, no polymorphism.

I can pass *AB.XY to the function, but that wouldn't make use of AB's Do2 function anymore. I could make an interface for it, which is probably the intended way, but if SomeFunc were to need near to all functions of XY, say getters of almost all fields, I'd basically need to create a copy of XY, as an interface (possible use case: I'm on a server and have to send values to a client in a specific way). I wouldn't want to only make it an interface, because I'd have to redefine all fields and functions in all types that are using the interface.

I can think of solutions for this problem, e.g. making Do2 a closure, that is set depending on the "host" type, or an enum field in XY and changing the behavior of Do2 based on that "type" variable, or using reflection and interface{} in SomeFunc, but I'd like to know what the "correct" way in Go would be. How do you do this in the most efficient way, even if you have a lot of functions?

答案1

得分: 5

在Go语言中正确的做法是使用接口(interfaces)。创建一个Do1Do2等的接口,并使SomeFunc与接口类型一起工作。

英文:

The correct way to do it in Go is to use interfaces. Create an interface of Do1, Do2, etc. and make SomeFunc work with the interface type.

答案2

得分: 4

像 @Ainar-G 所说的,使用接口是处理这种行为的正确方式,例如:

type Doer interface {
    Do1()
    Do2()
}

func (*S1) Do1() {
    println("S1.Do1")
}
func (*S1) Do2() {
    println("S1.Do2")
}

type S2 struct{ S1 }

func (*S2) Do1() {
    println("S2.Do1")
}

func DoIt(d Doer) {
    d.Do1()
    d.Do2()
    // 你可以使用类型断言来处理特定的情况,或者为你的类型实现 MarshalJson(或类似的)接口
    switch v := d.(type) {
        case *S1:
            println("S1 的特殊情况", v)
        case *S2:
            println("S2 的特殊情况", v)
    }
}

playground

英文:

Like @Ainar-G said, using an interface is the proper way for that kind of behavior, for example:

type Doer interface {
	Do1()
	Do2()
}

func (*S1) Do1() {
	println("S1.Do1")
}
func (*S1) Do2() {
	println("S1.Do2")
}

type S2 struct{ S1 }

func (*S2) Do1() {
	println("S2.Do1")
}

func DoIt(d Doer) {
	d.Do1()
	d.Do2()
	// you can use a type switch for specific corner cases, or implement a MarshalJson (or similar) interface for you types)
	switch v := d.(type) {
		case *S1:
			println("Special case for S1", v)
		case *S2:
			println("Special case for S2", v)
		
	}
}

<kbd>playground</kbd>

huangapple
  • 本文由 发表于 2014年8月11日 20:50:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/25243726.html
匿名

发表评论

匿名网友

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

确定