如何在Go中嵌入默认实现并引用嵌入类型

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

How to embed default implementations in go & refer to the embedding type

问题

我目前正在玩一些Go代码,并遇到了一个涉及嵌入的小问题,我找不到一个令人满意的答案。给定两种类型,其中一种嵌入另一种,并且满足一个接口,我希望嵌入的类型能够反射嵌入者的属性,以提供默认响应,这样对象就不必定义接口中的所有方法,除非它们想要覆盖。

在下面的示例中,我希望能够在嵌入中定义一个hello()函数,处理默认情况并返回Name(注意,这只是一个示例,真实代码更复杂和有用),而不需要对象显式定义接口上的所有方法。真实代码使用反射来推断对象类型的类名并构造名称,目前我正在调用基类助手函数并传入类型的实例,但这并不令人满意。

package main
type MyInterface interface {
    hello() string
    //...
}

type Embedded struct {

}

func (e *Embedded) hello() string {
    name := "none"
    // Would like to be able to return name of object here *if* embedded
    // What's the best approach to tackle this?
    
    return name
}


type Object struct {
    *Embedded
    Name    string
}

/*
// Would like an *optional* override, with default being embedded somehow replying with data from Object
func (o *Object) hello() string {
    return o.Name
}
*/


func main() {
    o := &Object{Name:"My Object Name"}
    println("Hello world",o.hello())
}

在goplay上的链接:

http://play.golang.org/p/ClOOCef9Zb

我对Go中解决这类问题的其他解决方案很感兴趣(提供使用嵌入类的属性的默认函数),以及解决这个特定问题的解决方案(如果有的话)。到目前为止,我唯一的解决方案是:

要求在满足此接口的所有类型上重新定义hello()方法,并放弃拥有提供默认方法的“基类”
使用指向对象实例的指针调用嵌入的辅助函数,以便类型可以具有大部分为空的方法调用嵌入类型。

如果有完全不同的方法在Go中是有价值的,并且不试图复制继承模型,我很想了解它们,到目前为止,这是我唯一一次错过继承的时候...

英文:

I'm currently playing around with some go code, and have run into a small problem involving embedding which I can't find a satisfactory answer to. Given two types, one of which embeds the other, and which satisfy an interface, I'd like the embedded type to be able to reflect on properties of the embedder in order to provide default responses, so that the object doesn't have to define all the methods in the interface, unless they want to override.

In the toy example below, I would like to be able to define a hello() func on embedded which deals with the default case and just returns Name (NB this is a toy example, the real code is more complex and useful), WITHOUT requiring that the object defines all the methods on the interface explicitly. The real code uses reflection to divine the class name of the object type and construct names, and currently I'm calling base class helpers passing in an instance of the type, but this doesn't feel satisfactory.

package main
type MyInterface interface {
	hello() string
	//...
}

type Embedded struct {

}

func (e *Embedded) hello() string {
	name := "none"
	// Would like to be able to return name of object here *if* embedded
	// What's the best approach to tackle this?
	
	return name
}


type Object struct {
	*Embedded
	Name	string
}

/*
// Would like an *optional* override, with default being embedded somehow replying with data from Object
func (o *Object) hello() string {
	return o.Name
}
*/


func main() {
	o := &Object{Name:"My Object Name"}
	println("Hello world",o.hello())
}

On goplay:

http://play.golang.org/p/ClOOCef9Zb

I'd be interested to hear of other solutions in Go to this sort of issue (providing default functions which use properties of the embedding class) as well as a solution to this particular problem (if there is one). The only solution I have so far is either:

Require a redefinition of method hello() on all types satisfying this interface, and just give up on having a 'base' class which provides default methods
Call embedded helper functions with a pointer to the object instance, so types can have mostly empty methods calling the embedded type.

If there are completely different approaches which would be valuable in Go and do not attempt to replicate the inheritance model I'd be interested to hear about them, so far this is the only time I've missed in inheritance...

答案1

得分: 2

一个方法的接收者是指类型T的实例。在执行T的方法时,没有语言支持的方法可以获取关于T被嵌入在U中的任何信息:

type (
        T foo
        U struct {
                t T
                f baz
        }
)

func bar() {
        var t T
        var u U
        t.foo()
        u.t.foo()
}

func (t T) foo()  {} // t可能被嵌入在u中,也可能没有
func (t *T) qux() {} // *t可能被嵌入在u中,也可能没有

你可能正在尝试使用结构化继承,但这是不支持的。通过嵌入来组合类型是一种方式,但这不类似于类层次结构。

另一方面,Go接口支持继承和方法重写,这可能是一种方法。只需注意,在这种情况下,继承是行为上的,而不是结构上的。

英文:

A method's receiver is a / refers to an instance of the respective type T. There's no language supported way how to obtain any information about T being embedded in, say U while executing T's method:

type (
        T foo
        U struct {
                t T
                f baz
        }
)

func bar() {
        var t T
        var u U
        t.foo()
        u.t.foo()
}

func (t T) foo()  {} // t might be or be not embeded in u
func (t *T) qux() {} // *t might be or be not embeded in u

You're probably trying to use structural inheritance. That's not supported. There is composition of types by embedding, but that's not something analogous to a class hierarchy.

OTOH, Go interfaces support inheritance and method overriding, so that may be the way. Just note that in this case the inheritance is behavioral, not structural.

答案2

得分: 0

为了解决这个问题,我决定放弃伪继承,并在接口中指定模型需要共享的信息(这里没有自动属性访问器对字段来说有点烦人,上面的对象必须显式导出这些字段,以便允许任何传递 MyInterface 引用的人使用它们,字段 Name 是公开的,但即使在接口上定义了 Name() string,也无法访问)。

然后,模型在另一个包中调用辅助函数,并在需要时将自身作为参数提供,由于符合 MyInterface,可以通过这种方式进行查询。因此,它(以及其他类似的 MyInterface 对象)可以使用其公共信息进行复杂操作,并充当外观。显然,在上面的简单示例中,这是无意义的,但我想分享一个包中多个 MyInterface 类型之间的代码,并且现在将采用这种方法而不是嵌入。

所以,正如上面的 jnml 指出的那样,我的主要收获是,嵌入对于混入依赖于实例状态的函数并不有用,并且不是继承层次结构中基类的直接替代品。如果你已经使用 Go 工作了一段时间,这一点显而易见...

英文:

To tackle this I've decided to ditch the ersatz inheritance, and specify the information the model needs to share in an interface (the lack of automatic attribute accessors for fields here is a little annoying, the object above has to explicitly export these to allow anyone passing a MyInterface ref around to use them, the field Name is public but not accessible even if Name() string is defined on the Interface).

The model then calls helper functions in another package, and supplies itself as a parameter where that is useful, and as it conforms to MyInterface it can be interrogated that way. So it (and other MyInterface objects like it) can have complex operations done using their public information and act as a facade. Obviously in the trivial example above this is pointless but I wanted to share code from a package between several MyInterface types, and will now go with this approach rather than embedding.

So my main takeaway here, as pointed out by jnml above, is that embedding is not useful for mixing in functions that rely on instance state, and is not a straightforward replacement for a base class in an inheritance hierarchy. Obvious perhaps if you have been working with Go for a while...

huangapple
  • 本文由 发表于 2013年3月17日 19:41:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/15460357.html
匿名

发表评论

匿名网友

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

确定