【问题标题】:return self struct clone in golang (without reflect)在 golang 中返回 self struct clone(没有反射)
【发布时间】:2017-03-02 09:13:44
【问题描述】:

有两个结构,
Foo 有一个Clone() 方法
Bar 继承自Foo

package main

import "fmt"

type IF interface {
    Clone() IF
}

type Foo struct {
    i int
}

func (this *Foo) Clone() IF {
    c := *this
    return &c
}

type Bar struct {
    Foo
}

func main() {
    t := &Bar{}
    c := t.Clone()
    fmt.Printf(`%T `, t)
    fmt.Printf(`%T `, c)
}

https://play.golang.org/p/pFn348aydW

输出是

*main.Bar *main.Foo

但我想克隆一个Bar,而不是Foo
我必须添加Bar.Clone()Foo.Clone() 完全相同

func (this *Bar) Clone() IF {
    c := *this
    return &c
}

https://play.golang.org/p/J6jT_0f1WW

现在输出是我想要的

*main.Bar *main.Bar

如果我会写很多像Bar 这样的结构,我不会写很多Clone(),我能做什么? 最好不要使用反射

【问题讨论】:

    标签: go


    【解决方案1】:

    Go 没有继承,这是组合,而不是继承,这就是你感到沮丧的原因。 Bar 没有继承自 Foo,它嵌入了 Foo,这是非常不同的。嵌入时,嵌入的方法作用于嵌入的结构,而不是包装器,所以是的,你是正确的,如果你想要返回 Bar 的东西,你必须添加一个 bar Clone()。

    也许最好退后一步考虑一下为什么要嵌入 Foo - 不要尝试将 Foo 用作基类,而应将其更多地视为您要导入的代码模块(自包含,指仅适用于 Foo 中的数据,而不适用于 Bar 中的数据)。所以很明显这只是一个玩具示例,但为了说明 Foo 的作用,可以对其进行扩展:

    type Foo struct {
        i int
    }
    
    func (f *Foo) String() string {
        if f.i > 0 {
            return fmt.Sprintf("val:%d", f.i)
        }
        return ""
    }
    
    type Bar struct {
        Foo
    }
    
    // Bar conforms to Stringer by virtue of embedding Foo
    // using the Foo data stored in i 
    type Stringer interface {
        String() string
    }
    
    func Print(b Stringer) {
        fmt.Printf("%s", b)
    }
    
    func main() {
        b := &Bar{}
        Print(b) // Outputs empty string
        b.i = 4
        Print(b) // Outputs val:4
    }
    

    https://play.golang.org/p/tNWPVw79aa

    所以您可以使用 Foo 方法,但它们应该只与 Foo 结构的内容相关,并且您可能应该将所有内容保留在 Bar 中,直到您非常确定出于某种原因需要 Foo,然后将其拆分 - Go 会引导您实现最小的复杂性,因此不支持继承。

    在支持继承的语言中,您可以通过生成具有抽象基类工厂等的可爱的大类分类来开始您的设计过程。在 go 中,您首先考虑附加到它的数据和行为或对其采取行动。

    【讨论】:

      【解决方案2】:

      组合与继承

      Go 不提供继承,而是 Go 提供组合。

      作曲

      在计算机科学中,对象组合是一种将简单对象或数据类型组合成更复杂对象的方法。

      继承

      在面向对象的编程中,继承是指一个对象或类基于另一个对象或类,使用相同的实现或指定新的实现以保持相同的行为。

      Go 组合实现

      可以将多个结构组合成一个组合,但每个方法都无法访问外部结构或其他组合结构。外部结构也可以访问内部结构。您应该将外部(非父)结构视为内部(非子)结构的包装器。

      Effective Go 说:

      嵌入与子类化有一个重要的区别。当我们嵌入一个类型时,该类型的方法成为外部类型的方法,但是当它们被调用时,方法的接收者是内部类型,而不是外部类型。

      由于您尝试做的事情是不可能的,请查看与您的大部分相同的snippet,但具有更清晰和惯用的设计(我相信它可以为您提供正确的实施思路):

      type IF interface {
          DoSomething()
      }
      
      type Foo struct {
          i int
      }
      
      func (f *Foo) DoSomething() {}
      
      type Cloner struct {
          *Foo
      }
      
      func (this *Cloner) Clone() IF {
          c := *this.Foo
          return &c
      }
      
      func main() {
          f := new(Foo)
          t := &Cloner{f}
          c := t.Clone()
          fmt.Printf("%T \n", t)
          fmt.Printf("%p \n", f)
          fmt.Printf("%p ", c)
      } 
      

      【讨论】:

      • 他没有返回指向同一个对象的指针,而是使用c := *this 行复制它。这将创建一个新变量并将其设置为等于this 指向的对象的副本(即实际对象,而不是指针)。示例:play.golang.org/p/YFQjbrZ3-7
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-11
      • 2017-08-15
      • 1970-01-01
      • 2020-09-28
      • 2019-06-24
      • 1970-01-01
      • 2017-03-26
      相关资源
      最近更新 更多