return self struct clone in golang (without reflect)

huangapple go评论71阅读模式
英文:

return self struct clone in golang (without reflect)

问题

有两个结构体,
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)
}

输出结果是

*main.Bar *main.Foo

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

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

现在输出结果是我想要的

*main.Bar *main.Bar

如果我要编写很多像 Bar 这样的结构体,我不想写很多 Clone() 方法,我该怎么办?
最好不要使用反射。

英文:

there are two structs,
Foo has a Clone() method
Bar is inherit from 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

output is

*main.Bar *main.Foo

but I want clone a Bar, not Foo
I must add Bar.Clone() exactly the same as Foo.Clone()

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

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

Now the output is what I want

*main.Bar *main.Bar

If I will write lots of struct like Bar, I won't write lots of Clone(), what I can do ?
It is best not to use reflect

答案1

得分: 4

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
}

// 通过嵌入Foo,Bar符合Stringer接口
// 使用存储在i中的Foo数据
type Stringer interface {
    String() string
}

func Print(b Stringer) {
    fmt.Printf("%s", b)
}

func main() {
    b := &Bar{}
    Print(b) // 输出空字符串
    b.i = 4
    Print(b) // 输出val:4
}

所以你可以使用Foo的方法,但它们只应与Foo结构体的内容相关,并且在你非常确定需要Foo的某些原因之前,你应该将所有内容都保留在Bar中,因为Go会引导你朝着最小复杂性的方向发展,并且不支持继承。

在支持继承的语言中,你可能会通过创建一个庞大的类分类法来开始设计过程,其中包括抽象基类、工厂等。在Go中,你首先考虑与数据相关的行为或作用于数据的行为。

英文:

Go doesn't have inheritance, this is composition, not inheritance, which is why you're getting frustrated. Bar doesn't inherit from Foo, it embeds Foo, which is very different. When embedding, the embedded methods act on the embedded struct, not the wrapper, so yes you're correct, you must add a bar Clone() if you want something returning a Bar.

Probably it's better to step back and consider why you're embedding Foo though - don't try to use Foo like a base class, think of it more as a module of code you're importing (self-contained, refers only to data in Foo, not in Bar). So obviously this is just a toy example, but to extend it to illustrate what Foo does:

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

So you can use Foo methods but they should only relate to contents of the Foo struct, and you should probably just keep everything in Bar till you're very sure you need Foo for some reason, then break it out - Go will guide you towards minimal complexity, and does not support inheritance for that reason.

In languages supporting inheritance you might start your design process by producing a lovely big taxonomy of classes with abstract base classes factories etc. In go you start by considering the data and behaviour attached to it or acting on it.

答案2

得分: 1

组合 vs 继承

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

组合

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

继承

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

Go的组合实现

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

《Effective Go》中提到:

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

由于你尝试的操作是不可能的,请参考与你的代码片段大致相同但设计更清晰和惯用的代码片段(我相信它可以给你正确的实现思路):

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)
} 
英文:

Composition vs inheritance

Go does not provide inheritance instead of it Go provides composition.

Composition:

> In computer science, object composition is a way to combine simple objects or data types into more complex ones.

Inheritance:

> In object-oriented programming, inheritance is when an object or class is based on another object or class, using the same implementation or specifying a new implementation to maintain the same behavior.

Go composition implementation

It is possible to combine several structs into single composition but each method hasn't access to outer struct or other composed structs. Also outer struct has access to inner structs. You should treat outer (not parent) struct as wrapper for inner (not child) structs.

Effective Go says:

> There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one.

Since what you tried to do is impossible, please see mostly the same snippet as yours, but with more clear and idiomatic design (I believe it could give you a right idea for the implementation):

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

huangapple
  • 本文由 发表于 2017年3月2日 17:13:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/42551141.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定