问题
I have some example code in which I declare a type foo
with some methods which call each other (say: foo.get
, which is called by foo.double
and foo.toString
).
I have another type, bar
, which embeds foo
and redefines get
. I am forced to redefine double
and toString
on bar
, so they can see bar.get
(rather than only foo.get
), but the body of these functions is basically identical to the original.
Is there a better way to organise this code, to avoid the redundancy while still having bar
fulfill the same interfaces as foo
?
Notes:
- The code as organised above works fine; it's just difficult to maintain because when I go to redefine a method originally declared on
foo
on an type that embedsfoo
I have to check carefully to see which other methods onfoo
call it, and make sure to redefine all of those too. It has proven to be very easy to accidentally miss one. - In the real project on which this is based there are nearly a dozen types which embed '
foo
', and a similar number of inter-connected methods on each type. - I could add an interface-valued member to foo containing a pointer to the outermost enclosing struct that embeds it, and have
foo.double
callf.outermost.get()
instead off.get()
, but this seems a little wasteful and would mean that zero values offoo
were not valid. (Plus it is only possible if the embedded type is a struct.)
回答1:
In Go there is embedding, but there is no polymorphism. If you embed a type in a struct, all the methods of the embedded type get promoted and will be in the method set of the wrapper struct type. But you can't "override" the promoted methods. Sure, you can add your own method with the same name, and calling a method by that name on the wrapper struct will invoke your method, but if this method is called from the embedded type, that will not be dispatched to your method, it will still call the "original" method that was defined to the embedded type.
Read more about this here: Does fragile base class issue exist in Go? and here: Go embedded struct call child method instead parent method.
It looks like you only want to "inherit" the double()
and toString()
methods (which should be called String()
in Go), but not the get()
method as its implementation changes from type-to-type.
So basically you should refactor a little. Your foo
type should have / get a value that provides the get()
method. You can capture this with a getter
interface:
type getter interface {
get() int
}
And the foo
implementation:
type foo struct {
g getter
}
func (f foo) double() int {
return f.g.get() * 2
}
func (f foo) toString() string {
return fmt.Sprintf("%d", f.g.get())
}
And a bar
type which embeds foo
, and only provides the "missing" get()
:
type bar struct {
foo
}
func (b bar) get() int {
return 69
}
Usage example:
b := bar{}
b.foo = foo{g: b}
fmt.Println(b.double())
fmt.Println(b.toString())
Output is as expected (try it on the Go Playground):
138
69
With a simple function value
Using the above getter
interface is nice, as it provides flexibility in the future, should you need to add other methods to it.
If this is not the case and all you need is a single function, you may leave out the interface and just use a function value.
This is how it could look like:
type foo struct {
get func() int
}
func (f foo) double() int {
return f.get() * 2
}
func (f foo) toString() string {
return fmt.Sprintf("%d", f.get())
}
type bar struct {
foo
}
func (b bar) get() int {
return 69
}
And using it:
b := bar{}
b.foo = foo{get: b.get}
fmt.Println(b.double())
fmt.Println(b.toString())
Output is the same. Try it on the Go Playground.
Note that while we used the bar.get()
method (b.get
method value), it is not a requirement that get()
should be a method. It can be an ordinary function value, or even a function literal, such as:
b := bar{foo: foo{get: func() int { return 69 }}}
fmt.Println(b.double())
fmt.Println(b.toString())
Try this one on the Go Playground.
来源:https://stackoverflow.com/questions/44526957/how-can-i-organise-this-go-code-that-redefines-methods-from-an-embedded-type-to