英文:
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 不是太多,那么实现单独的集合类型,比如 UserCollection
、BarCollection
、FooCollection
,是一个选择。
另一种方式是使用泛型空接口类型 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 "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())
}
</pre>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论