是否可以在golang中调用父结构的重写方法?

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)

  • 确实比其他一切更容易遵循+1 (2认同)

chm*_*ike 6

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


met*_*lim 6

我自己一直在为此挣扎。找到2个解决方案:

  1. 惯用的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

  1. 类似于您的第二个选择:接口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

  • 非常有趣,谢谢,但我没有看到对具有父类型的变量的子方法的任何实际调用 (2认同)