【问题标题】:Golang Method OverrideGolang 方法覆盖
【发布时间】:2016-06-30 13:00:37
【问题描述】:

我有以下几点:

type Base struct {

}

func(base *Base) Get() string {
    return "base"
}

func(base *Base) GetName() string {
    return base.Get()
}

我想用新的 Get() 实现定义一个新类型,这样我就可以使用新类型代替 Base 并且在调用 GetName() 的地方调用新的 Get() 实现。如果我使用 Java,我将继承 Base 并覆盖 Get()。我应该如何在 Go 中实现这一点?如果可能,我想避免破坏性更改,因此不需要更改 Base 的现有使用者。

我的第一次尝试看起来像......

type Sub struct {
    Base
}

func(sub *Sub) Get() string {
    return "Sub"
}

..这不起作用。我的大脑还没有完全适应 Go。

【问题讨论】:

标签: inheritance go overriding


【解决方案1】:

Go 不是“经典”OO 语言:它不知道类和继承的概念。

但是它确实包含了非常灵活的接口概念,通过它可以提供面向对象的许多方面。 Go 中的接口提供了一种指定对象行为的方法:如果有东西可以做到这一点,那么它就可以在这里使用。

接口定义了一组方法,但这些方法不包含代码:它们没有实现(这意味着它们是抽象的)。

所以你可以实现在同一个方法中使用不同类型的方法是使用接口。

下面是一个简单的例子来证明它:

package main

import (
    "fmt"
)

type Base struct{}
type Baser interface {
    Get() float32
}

type TypeOne struct {
    value float32
    Base
}

type TypeTwo struct {
    value float32
    Base
}

type TypeThree struct {
    value float32
    Base
}

func (t *TypeOne) Get() float32 {
    return t.value
}

func (t *TypeTwo) Get() float32 {
    return t.value * t.value
}

func (t *TypeThree) Get() float32 {
    return t.value + t.value
}

func main() {
    base := Base{}
    t1 := &TypeOne{1, base}
    t2 := &TypeTwo{2, base}
    t3 := &TypeThree{4, base}

    bases := []Baser{Baser(t1), Baser(t2), Baser(t3)}

    for s, _ := range bases {
        switch bases[s].(type) {
        case *TypeOne:
            fmt.Println("TypeOne")
        case *TypeTwo:
            fmt.Println("TypeTwo")
        case *TypeThree:
            fmt.Println("TypeThree")
        }

        fmt.Printf("The value is:  %f\n", bases[s].Get())
    }   
}

Go Playground

【讨论】:

  • 我认为这不能回答问题。他想实例化子类,调用 GetName 并实际调用子类方法。这个例子没有尝试解决这个问题。
  • 重写方法在哪里?你演示了不同的接口实现。
  • Go 通过封装其他类型有继承的概念。所有字段和方法都暴露在最终类型上,但也可以直接访问。
  • 如果 Base 不提供任何字段或方法,为什么要封装它?它可以被移除而没有副作用。
【解决方案2】:

接口是方法签名的命名集合:
见:https://gobyexample.com/interfaces
和:http://www.golangbootcamp.com/book/interfaces
所以最好不要以这种面向对象的方式使用。


你问的不是 Golang 惯用的 但可能(2 种方式):

这就是您所需要的(工作示例代码):

package main

import "fmt"

type Base struct {
}

func (base *Base) Get() string {
    return "base"
}

type Getter interface {
    Get() string
}

func (base *Base) GetName(getter Getter) string {
    if g, ok := getter.(Getter); ok {
        return g.Get()
    } else {
        return base.Get()
    }
}

// user code :
type Sub struct {
    Base
}

func (t *Sub) Get() string {
    return "Sub"
}
func (t *Sub) GetName() string {
    return t.Base.GetName(t)
}

func main() {
    userType := Sub{}
    fmt.Println(userType.GetName()) // user string
}

输出:

Sub

如您所见,必须在用户代码中完成一个初始化代码:

func (t *Sub) GetName() string {
    return t.Base.GetName(t)
}

这也有效:

package main

import "fmt"

type Getter interface {
    Get() string
}

type Base struct {
    Getter
}

func (base *Base) Get() string {
    return "base"
}

func (base *Base) GetName() string {
    return base.Getter.Get()
}

// user code :
type Sub struct {
    Base
}

func (t *Sub) Get() string {
    return "Sub"
}
func New() *Sub {
    userType := &Sub{}
    userType.Getter = interface{}(userType).(Getter)
    return userType
}

func main() {
    userType := New()
    fmt.Println(userType.GetName()) // user string
}

输出:

Sub

如您所见,必须在用户代码中完成一个初始化代码:

userType.Getter = interface{}(userType).(Getter)

【讨论】:

  • Go 让我哭得这么厉害。编码语言数十年的进步被遗忘。
  • 如果我包含一个定义 Base 结构但没有 Getter 接口的库怎么办。我无法更改库代码。所以让我们假设,我们没有接口。只是我们想要“覆盖”的 Base 结构。是的,我知道,Golang 没有“覆盖、多态等”,你知道,我的意思是,只是组合。
  • @Kangur,我认为相反,我们从来不需要覆盖方法,我们不知道!我们真正需要的只是一个接口和实现者。没有它真的更好,覆盖方法在机器上很重,但是接口都是在编译时计算的,程序在运行时没有额外的内存来决定调用什么方法。我们需要做的就是改变我们的文化,开始更多地关注接口而不是使用超类型
  • @wellsantos 在编写了几个月的 golang 代码后,我同意了。由于在编译时绑定了所有方法,我知道 Go 在抽象概念和调用速度之间取得了很好的平衡。我现在喜欢它,但正如你所说,必须放弃或适应一些旧方法。
  • 可能是菜鸟问题:是什么阻止我用我自己的本地定义替换某些“接口”方法的库实现? “重复方法”链接器错误或...?
【解决方案3】:

您可以通过以下方式“覆盖”方法并从封装结构中访问它:

package main

import (
    "fmt"
)

type Getter interface {
    Get() string
}

type Base struct {
}

type Sub1 struct {
    Base // reuse fields and methods
}

type Sub2 struct {
    Base // reuse fields and methods
}

func(base *Base) Get() string {
    return "getBase" 
}

func(sub1 *Sub1) Get() string {
    return "getSub1" // override base method
}


func(sub1 Sub1) CallOverriden() string {
    return sub1.Get()
}

func(sub1 Sub1) CallBase() string {
    return sub1.Base.Get() // access and expose encapsulated/embedded method
}


func main() {
    var g Getter
    g = &Sub1{}
    
    fmt.Println(g.Get()) // getSub1
    
    g = &Sub2{}
    fmt.Println(g.Get()) // getBase // not-overriden method
    
    fmt.Println(Sub1{}.CallOverriden())
    fmt.Println(Sub1{}.CallBase()) // "base" method can be exposed
}

【讨论】:

  • 该方法从外部起作用,但不能从内部起作用,例如让 Base 的其他方法调用 Get 并允许派生/封闭结构覆盖该行为。
  • 我相信这在 Golang @jiping-s 中是不可能的。 Base 的其他方法指定了他们所知道的 - 基于其他方法。他们不知道派生结构,除非您明确指定它。如果您需要基础的此方法对不同的对象进行操作,则可以将其作为参数传递。 func(base *Base) Get(do Doer) string { return do.DoIt() + "base processing" }。然后,您可以传递任何类型的实现 Doer 接口的结构(实现 = 具有与 Doer 相同定义的方法)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-21
  • 1970-01-01
  • 2016-03-06
  • 2018-03-27
  • 2014-05-27
  • 2014-01-04
相关资源
最近更新 更多