简体   繁体   中英

Overwriting behavior of embedded struct

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, eg 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?

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.

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)

    }
}

playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM