zha*_*zhi 49 oop inheritance overriding go
我想实现这样的代码,其中B继承自A并且只覆盖A的Foo()方法,我希望代码打印B.Foo(),但它仍然打印A.Foo(),似乎接收器在Golang在C++中无法像这样工作,在启用动态绑定的情况下,代码可以像我想要的那样工作.
我还发布了另一段代码,但它实现起来太难了,而且更像是一种黑客方式,我认为它不是Golang风格.
所以我的问题是:如果父的Bar()方法有一些逻辑,例如,打开一个文件,然后读取一些行,并使用Foo()将这些行输出到stdout,并将Child(在示例中为B)想要使用它们中的大多数,唯一的区别是Child希望Foo()将行输出到另一个文件.我该如何实施呢?我听说Golang的继承不能像C++或Java那样工作,Golang中的正确方法是什么?
package main
import (
"fmt"
)
type A struct {
}
func (a *A) Foo() {
fmt.Println("A.Foo()")
}
func (a *A) Bar() {
a.Foo()
}
type B struct {
A
}
func (b *B) Foo() {
fmt.Println("B.Foo()")
}
func main() {
b := B{A: A{}}
b.Bar()
}
output: A.Foo()
Run Code Online (Sandbox Code Playgroud)
以下作品有效,但写作时
a := A{}
a.Bar()
Run Code Online (Sandbox Code Playgroud)
您将遇到编译器错误
package main
import (
"fmt"
)
type I interface {
Foo()
}
type A struct {
i I
}
func (a *A) Foo() {
fmt.Println("A.Foo()")
}
func (a *A) Bar() {
a.i.Foo()
}
type B struct {
A
}
func (b *B) Foo() {
fmt.Println("B.Foo()")
}
func main() {
b := B{A: A{}}
b.i = &b // here i works like an attribute of b
b.Bar()
output: B.Foo()
Run Code Online (Sandbox Code Playgroud)
Not*_*fer 19
正如你所写的那样,Go所拥有的并不是真正的继承,允许像特征一样继承的方法称为嵌入.
http://golang.org/doc/effective_go.html#embedding
它的意思基本上是嵌入式结构不知道它是嵌入的,所以你不能覆盖从它调用的任何东西.您实际上可以使用嵌入式结构并仅从嵌入结构中获取它的引用.
所以你最好的方法就是你的第二个例子 - 通过使用接口的某种依赖注入.ie - A引用某些接口来执行实际工作,比如worker写入文件或其他任何东西.然后在实例化B时,你也worker用另一个工人替换A (即使没有嵌入A,你也可以做到).A只是在myWorker.Work()不关心工人是什么的情况下做的事情.
小智 9
最近我需要这样做,OP提出的组合方法效果很好。
我尝试创建另一个示例来尝试演示父子关系并使其更易于阅读。
https://play.golang.org/p/9EmWhpyjHf:
package main
import (
"fmt"
"log"
)
type FruitType interface {
Wash() FruitType
Eat() string
}
type Fruit struct {
name string
dirty bool
fruit FruitType
}
func (f *Fruit) Wash() FruitType {
f.dirty = false
if f.fruit != nil {
return f.fruit
}
return f
}
func (f *Fruit) Eat() string {
if f.dirty {
return fmt.Sprintf("The %s is dirty, wash it first!", f.name)
}
return fmt.Sprintf("%s is so delicious!", f.name)
}
type Orange struct {
*Fruit
}
func NewOrange() *Orange {
ft := &Orange{&Fruit{"Orange", true, nil}}
ft.fruit = ft
return ft
}
func NewApple() *Fruit {
ft := &Fruit{"apple", true, nil}
return ft
}
func (o *Orange) Eat() string {
return "The orange is so sour!"
}
func main() {
log.Println(NewApple().Eat())
log.Println(NewApple().Wash().Eat())
log.Println(NewOrange().Eat())
log.Println(NewOrange().Wash().Eat())
}
Run Code Online (Sandbox Code Playgroud)
Go 不支持虚方法重写。因此,Go 并不直接支持您想要使用的设计模式。这被认为是不好的做法,因为更改 A.Bar() 的实现会破坏所有派生类,例如假设 A.Foo() 将由 A.Bar() 调用的 B。您想要使用的设计模式将使您的代码变得脆弱。
在 Go 中执行此操作的方法是使用 Foo() 方法定义 Fooer 接口。Fooer 将作为参数传递给 Bar() 或存储在 A 的字段中并由 A.Bar() 调用。要更改 Foo 操作,请更改 Fooer 值。这称为组合,它比通过继承和方法重写来改变 Foo 操作要好得多。
这是在 Go 中执行您想做的事情的惯用方法: https: //play.golang.org/p/jJqXqmNUEHn。在此实现中,Fooer 是 A 的成员字段,由实例工厂函数 的参数初始化NewA()。当 Fooer 在 A 的生命周期内不经常更改时,这种设计模式是更可取的。否则,您可以将 Fooer 作为方法的参数传递Bar()。
这就是我们改变 Go 中的行为的方式Foo()。Bar()之所以称为组合,是因为您可以通过更改组成 A 的实例来更改行为。
package main
import (
"fmt"
)
type Fooer interface {
Foo()
}
type A struct {
f Fooer
}
func (a *A) Bar() {
a.f.Foo()
}
func NewA(f Fooer) *A {
return &A{f: f}
}
type B struct {
}
func (b *B) Foo() {
fmt.Println("B.Foo()")
}
type C struct {
}
func (c *C) Foo() {
fmt.Println("C.Foo()")
}
func main() {
a := NewA(new(B))
a.Bar()
a.f = &C{}
a.Bar()
}
Run Code Online (Sandbox Code Playgroud)
PS:以下是您出于文档目的想要实现的设计模式的可能实现:https://play.golang.org/p/HugjIbYbout
我自己一直在为此挣扎。找到2个解决方案:
惯用的Go方式:实现通用方法,即调用“虚拟”方法,作为带有接口作为参数的外部函数。
package main
import "fmt"
type ABCD interface {
Foo()
}
type A struct {
}
func (a *A) Foo() {
fmt.Println("A.Foo()")
}
type B struct {
A
}
func (b *B) Foo() {
fmt.Println("B.Foo()")
}
// Bar is common "method", as external function.
func Bar(a ABCD) {
a.Foo()
}
func main() {
b := &B{} // note it is a pointer
// also there's no need to specify values for default-initialized fields.
Bar(b) // prints: B.Foo()
}
Run Code Online (Sandbox Code Playgroud)在Go Playground上尝试一下:https://play.golang.org/p/FF4fdvTCRAo
类似于您的第二个选择:接口hackery。但是,由于Bar()并不是特定于A的(它是A和B所共有的),因此让我们将其移至基类,并隐藏实现细节和所有危险的东西:
package main
import "fmt"
//////////////////////////////////////////////////////////////////////
// Implementation.
// aBase is common "ancestor" for A and B.
type aBase struct {
ABCD // embed the interface. As it is just a pointer, it has to be initialized!
}
// Bar is common to A and B.
func (a *aBase) Bar() {
a.Foo() // aBase has no method Foo defined, so it calls Foo method of embedded interface.
}
// a class, not exported
type a struct {
aBase
}
func (a *a) Foo() {
fmt.Println("A.Foo()")
}
// b class, not exported
type b struct {
aBase
}
func (b *b) Foo() {
fmt.Println("B.Foo()")
}
//////////////////////////////////////////////////////////////////////
// Now, public functions and methods.
// ABCD describes all exported methods of A and B.
type ABCD interface {
Foo()
Bar()
}
// NewA returns new struct a
func NewA() ABCD {
a := &a{}
a.ABCD = a
return a
}
// NewB returns new struct b
func NewB() ABCD {
b := &b{}
b.ABCD = b
return b
}
func main() {
b := NewB()
b.Bar() // prints: B.Foo()
a := NewA()
a.Bar() // prints: A.Foo()
}
Run Code Online (Sandbox Code Playgroud)在Go Playground上尝试:https : //play.golang.org/p/0Zcs_arturP
| 归档时间: |
|
| 查看次数: |
35326 次 |
| 最近记录: |