英文:
Golang and inheritance
问题
我想在我的库中提供一个带有方法的基本结构体,可以进行“扩展”。
这个基本结构体的方法依赖于扩展结构体的方法。
在Go语言中,这是不直接可能的,因为结构体方法只能访问自己的字段,而不能访问父结构体的字段。
关键是拥有功能,我不必在每个扩展类中重复编写。
我想出了这个模式,它工作得很好,但由于其循环结构,看起来相当复杂。
我在其他Go代码中从未找到过类似的东西。
这是否非常不符合Go语言的风格?
我可以采取什么不同的方法?
type MyInterface interface {
SomeMethod(string)
OtherMethod(string)
}
type Base struct{
B MyInterface
}
func (b *Base) SomeMethod(x string) {
b.B.OtherMethod(x)
}
type Extender struct {
Base
}
func (b *Extender) OtherMethod(x string) {
// Do something...
}
func NewExtender() *Extender {
e := Extender{}
e.Base.B = &e
return &e
}
英文:
I want to provide a base struct with methods in my library that can be 'extended'.
The methods of this base struct rely on methods from the extending struct.
This is not directly possible in Go, because struct methods only have acces to the structs own fields, not to parent structs.
The point is to have functionality that I do not have to repeat in each extending class.
I have come up with this pattern, which works fine,
but looks quite convoluted due to it's cyclical structure.
I have never found anything like it in other Go code.
Is this very un-go?
What different approach could I take?
type MyInterface interface {
SomeMethod(string)
OtherMethod(string)
}
type Base struct{
B MyInterface
}
func (b *Base) SomeMethod(x string) {
b.B.OtherMethod(x)
}
type Extender struct {
Base
}
func (b *Extender) OtherMethod(x string) {
// Do something...
}
func NewExtender() *Extender {
e := Extender{}
e.Base.B = &e
return &e
}
答案1
得分: 41
正如人们在评论中提到的那样,Go 语言鼓励组合而非继承。
为了解决你关于减少代码重复的问题,你可以使用嵌入。
使用上面链接的 Effective Go 中的示例,你可以从只执行少数操作的非常狭窄的接口开始:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
然后,你可以将接口组合成另一个接口:
// ReadWriter 是将 Reader 和 Writer 接口组合在一起的接口。
type ReadWriter interface {
Reader
Writer
}
对于结构体,也可以类似地组合实现了 Reader 和 Writer 接口的结构体:
type MyReader struct {}
func (r *MyReader) Read(p []byte) (n int, err error) {
// 实现了 Reader 接口。
}
type MyWriter struct {}
func (w *MyWriter) Write(p []byte) (n int, err error) {
// 实现了 Writer 接口。
}
// MyReadWriter 存储指向 MyReader 和 MyWriter 的指针。
// 它实现了 ReadWriter 接口。
type MyReadWriter struct {
*MyReader
*MyWriter
}
基本上,任何实现了 Reader
或 Writer
的东西都可以通过将它们组合在一个结构体中来重用,并且该外部结构体将自动实现 ReadWriter
接口。
这基本上就是在进行依赖注入,对于测试也非常有用。
上面结构体代码的示例:
func (rw *MyReadWriter) DoCrazyStuff() {
data := []byte{}
// 做一些操作...
rw.Read(data)
rw.Write(data)
// 你懂的...
}
func main() {
rw := &MyReadWriter{&MyReader{}, &MyWriter{}}
rw.DoCrazyStuff()
}
需要指出的一点与其他语言的组合范式稍有不同,即 MyReadWriter
结构体现在可以同时充当 Reader
和 Writer
。这就是为什么在 DoCrazyStuff()
中我们使用 rw.Read(data)
而不是 rw.Reader.Read(data)
的原因。
更新:修正了示例中的错误。
英文:
As mentioned in people's comments, Go encourages composition over inheritance.
To address your question about reducing code duplication, you would want to use embedding.
Using the example from Effective Go linked above, you start with very narrow interfaces that only do a few things:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
Then you can either compose interfaces together into another interface:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
It works similarly for structs, where you can compose structs that implement Reader and Writer together in another struct:
type MyReader struct {}
func (r *MyReader) Read(p []byte) (n int, err error) {
// Implements Reader interface.
}
type MyWriter struct {}
func (w *MyWriter) Write(p []byte) (n int, err error) {
// Implements Writer interface.
}
// MyReadWriter stores pointers to a MyReader and a MyWriter.
// It implements ReadWriter.
type MyReadWriter struct {
*MyReader
*MyWriter
}
Basically, anything that implements a Reader
or a Writer
can be reused by composing them together in a struct, and that outer struct will automatically implement the ReadWriter
interface.
This is basically doing Dependency Injection, and it's super useful for testing too.
Example from the struct code above:
func (rw *MyReadWriter) DoCrazyStuff() {
data := []byte{}
// Do stuff...
rw.Read(data)
rw.Write(data)
// You get the idea...
}
func main() {
rw := &MyReadWriter{&MyReader{}, &MyWriter{}}
rw.DoCrazyStuff()
}
One thing to point out that's slightly different from other languages' composition paradigm is that the MyReadWriter
struct can now act as both a Reader
and a Writer
. That's why in DoCrazyStuff()
we do rw.Read(data)
instead of rw.Reader.Read(data)
.
UPDATE: Fixed incorrect example.
答案2
得分: 4
很抱歉让你失望,但你问错了问题。当我开始写Go代码时,我也遇到了类似的问题。
你不能简单地将一个类层次结构翻译成Go代码,至少不能得到令人满意的结果。通常在Go中解决这类问题有一种非常优雅和简单的方式,但要发现它们,你需要以与你习惯不同的方式思考。
不幸的是,你的问题并没有说明你试图解决的是什么问题。你只是描述了你想要如何解决它。因此,我有点不愿意给出一个通用的答案,因为它不会导致符合Go语言习惯的代码。我理解如果你对这个答案感到失望,但在我看来,这是你能得到的最有价值的答案
英文:
Sorry to disappoint you, but you are asking the wrong question. I had a similar problem when I started writing Go code.
You can not simply take a class hierarchy and translate it to Go code, at least not with satisfying results. Usually there is a very elegant and simple way to solve such things in Go, but to discover them, you need to think a bit differently as you are used to.
Unfortunately, your question doesn't say anything about what problem you are trying to solve. You have just described how you would like to solve it. Therefore I am a bit reluctant to give a general answer, since it will not lead to idiomatic Go code. I understand if you are disappointed by that answer, but in my opinion, that`s the most value-able answer you can get
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论