嵌入类型和结构体的多态性

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

Embedded types and polymorphism with structs

问题

我正在创建一个API,并且我有两个不同的结构体用于JSON响应;一个用于单个记录,一个用于记录集合:

type Model struct {
    Id uint
}

type Collection struct {
    Records []Model
}

Model只是数据库数据的结构体表示(比如一个用户),而Collection是一组模型。

问题是,将会有单独的结构体嵌入Model类型,像这样:

type User struct {
    *Model
    Name string
}

由于User不满足Model类型,我无法像这样将其附加到Collection结构体中:

user := User{&Model{1}, "Jon"}
uc := &Collection{[]User{user}}

如何使Records结构体接受实现了Model的结构体类型呢?

英文:

I'm creating an API, and I have two different structs for JSON responses; one for single records and one for collection of records:

type Model struct {
  Id uint
}

type Collection struct {
  Records []Model
}

A Model is just a struct representation of database data (for say, a User), and a Collection is a collection of models.

The issue is that there will be separate structs that embed the Model type, like so:

type User struct {
	*Model
	Name string
}

And since a User doesn't satisfy the Model type, I can't append it to the Collection struct like this:

user := User{&Model{1}, "Jon"}
uc := &Collection{[]User{user}}

How can I make the Records struct accept struct types that implement Model?

答案1

得分: 8

你有两个选择:

  • 创建另一种集合类型
  • 使用接口

如果你的 foos 和 bars 不是太多,那么实现单独的集合类型,比如 UserCollectionBarCollectionFooCollection,是一个选择。

另一种方式是使用泛型空接口类型 interface{},它可以保存任意类型的值(因为每个类型的实例都满足空接口):

type Collection struct {
    Records []interface{}
}

这种方式有一些缺点,比如每次访问 Records 字段时都需要进行类型断言,可能会错误地将任何类型的值放入其中,而且大多数操作都需要使用反射。

更好的方式是使用接口来实现一个模型接口,并让每个模型类型都实现该接口:

type Model interface {
    ImplementsModel()
}

type BaseModel struct {
    Id uint
}

func (b *BaseModel) ImplementsModel() {}

type User struct {
    *BaseModel
    Name string
}

type Collection struct {
    Records []Model
}

当然,一旦你有了有意义的方法,你可以删除 ImplementsModel 这个虚拟方法。这个虚拟方法只是用来区分接口和空接口,以防止将整数值或字符串放入集合中代替实际的模型。

英文:

You have two options:

  • Make another collection type
  • Use interfaces instead

Implementing separate collections like UserCollection, BarCollection, FooCollection is an option if you do not have too many foos and bars.

The other way is to use either the generic empty interface type interface{} which will hold values of any type (since every instance of every type satisfies the empty interface):

type Collection struct {
    Records []interface{}
}

This has a number of drawbacks such as that you have to do a type assertion every time you access the Records field, that you can put everything in there by mistake and that you have to use reflection to do most manipulations.

The better way to use interfaces would be to implement a model interface and let every type that is a model implement the interface:

type Model interface {
    ImplementsModel()
}

type BaseModel struct {
    Id uint
}

func (b *BaseModel) ImplementsModel() {}

type User struct {
    *BaseModel
    Name string
}

type Collection struct {
    Records []Model
}

Of course you can remove the ImplementsModel dummy method once you have meaningful methods. This dummy method is just there to distinguish the interface from the empty interface so that you cannot put integer values or strings in place of actual models into the collection.

答案2

得分: 4

模型结构不是用户结构的类型。
即使嵌入了模型,用户也不能像模型一样行为。

在这种情况下,您可以使用以下类似的接口。
http://play.golang.org/p/Hh0zIT9yFC

package main

import "fmt"

type Model interface {
    GetID() uint
}

type BaseModel struct {
    Id uint
}

func (m BaseModel) GetID() uint {
    return m.Id
}

type Collection struct {
    Records []Model
}

type User struct {
    *BaseModel
    Name string
}

func (u User) GetName() string {
    return u.Name
}

func main() {
    user := User{&BaseModel{1}, "Jon"}
    uc := &Collection{[]Model{user}}
    u := uc.Records[0].(User)
    fmt.Println(u.GetID())
    fmt.Println(u.GetName())
}
英文:

Model struct is not a type User struct.
User can't behave Model even if embedded it.

This case, you can use interface like below.
http://play.golang.org/p/Hh0zIT9yFC

<pre>
package main

import &quot;fmt&quot;

type Model interface {
    GetID() uint
}

type BaseModel struct {
	Id uint
}

func (m BaseModel) GetID() uint {
    return m.Id
}

type Collection struct {
    Records []Model
}

type User struct {
    *BaseModel
    Name string
}

func (u User) GetName() string {
    return u.Name
}

func main() {
    user := User{&amp;BaseModel{1}, &quot;Jon&quot;}
    uc := &amp;Collection{[]Model{user}}
    u := uc.Records[0].(User)
    fmt.Println(u.GetID())
    fmt.Println(u.GetName())
}

</pre>

huangapple
  • 本文由 发表于 2015年10月7日 07:49:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/32981576.html
匿名

发表评论

匿名网友

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

确定