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

问题描述 投票:43回答:5

我想实现这样的代码,其中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()

以下作品有效,但写作时

a := A{}
a.Bar()

您将遇到编译器错误

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()
oop inheritance go override
5个回答
2
投票

最近我需要这样做,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())
}

2
投票

我自己也在苦苦挣扎。找到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() }

在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() }

在Go Playground尝试:https://play.golang.org/p/0Zcs_arturP


1
投票

Go不支持虚方法覆盖。因此,Go不直接支持您要使用的设计模式。这被认为是不好的做法,因为更改A.Bar()的实现会破坏所有派生类,如B,假设A.Boo()将调用A.Foo()。您要使用的设计模式将使您的代码变得脆弱。

在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()行为的方式。它被称为组合,因为您通过更改组成A的实例来更改Bar()的行为。

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()
}

PS:以下是您希望为文档目的实现的设计模式的可能实现:https://play.golang.org/p/HugjIbYbout

© www.soinside.com 2019 - 2024. All rights reserved.