英文:
How to model these types in Go
问题
我有多个类似这样的类型:
type QueryMessage struct {
Header MessageHeader
Type MessageType
Query SqlQuery
}
type UpdateMessage struct {
Header MessageHeader
Type MessageType
OldData map[string]interface{}
NewData map[string]interface{}
}
type InsertMessage struct {
Header MessageHeader
Type MessageType
Data map[string]interface{}
}
它们都有两个共同的属性,Header
和 Type
。最终,我需要将它们聚合成一个通用消息的数组。
目前,我的 Message
接口看起来是这样的:
type Message interface{}
所以,我基本上做的是这样的(将它们全部转换为 Message
接口):
q := QueryMessage{ ... }
u := UpdateMessage{ ... }
i := InsertMessage{ ... }
allMessages := [3]Message { Message(q), Message(u), Message(i) }
这样可以工作,但是它丢失了所有的类型信息,我希望仍然能够从 Message
类型中公开 Header
和 Type
(这样客户端代码理论上可以根据 Type
将 Message
强制转换回其原始类型)。
如何实现这一点?我找不到一个合适的方法,因为接口不能有属性,如果我将 Message
设为结构体,Go 就不允许我将 QueryMessage
强制转换为 Message
。
英文:
I have multiple types like this:
type QueryMessage struct {
Header MessageHeader
Type MessageType
Query SqlQuery
}
type UpdateMessage struct {
Header MessageHeader
Type MessageType
OldData map[string]interface{}
NewData map[string]interface{}
}
type InsertMessage struct {
Header MessageHeader
Type MessageType
Data map[string]interface{}
}
They all have two properties in common, Header
and Type
. In the end I need to aggregate them into an array of generic messages.
At the moment my Message
interface looks like this:
type Message interface {}
So what I basically do is something like this (casting them all to Message interface):
q := QueryMessage{ ... }
u := UpdateMessage{ ... }
i := InsertMessage{ ... }
allMessages := [3]Message { Message(q), Message(u), Message(i), }
This works, but it loses all the type information and I would like to be able to still expose Header
and Type
from the Message
type (so that client code theoretically could cast the Message
based on the Type
back to it's original type.
How can this be done? I couldn't figure out a proper way, interfaces can't have properties and if I make Message
a struct, Go doesn't allow me to cast e. g. a QueryMessage
to a Message
anymore.
答案1
得分: 4
您的接口可以提供访问器:
type Message interface {
GetHeader() MessageHeader
GetType() MessageType
}
然后您需要在所有类型上实现此接口。
为了提取出公共部分,您可以使用额外的基本类型,并将其嵌入到其他结构体中:
// 接口:
type Message interface {
GetHeader() MessageHeader
GetType() MessageType
}
// 基本结构体类型,包含2个字段并实现Message接口:
type baseMessage struct {
Header MessageHeader
Type MessageType
}
func (b *baseMessage) GetHeader() MessageHeader {
return b.Header
}
func (b *baseMessage) GetType() MessageType {
return b.Type
}
// 三个类型,其中包含嵌入的"baseMessage"部分:
type QueryMessage struct {
baseMessage
Query SqlQuery
}
type UpdateMessage struct {
baseMessage
OldData map[string]interface{}
NewData map[string]interface{}
}
type InsertMessage struct {
baseMessage
Data map[string]interface{}
}
// 编译时检查:所有上述类型都实现了Message接口:
var _ Message = &QueryMessage{}
var _ Message = &UpdateMessage{}
var _ Message = &InsertMessage{}
链接:https://play.golang.org/p/Uho_2loXpZ
英文:
Your interface can provide accessors :
type Message interface {
GetHeader() MessageHeader
GetType() MessageType
}
You then have to implement this interface on all your types.
To factor out the common part, you can use an extra base type, and embed it in your other structs :
// the interface :
type Message interface {
GetHeader() MessageHeader
GetType() MessageType
}
// a base struct type, which holds 2 fields and implements the Message interface :
type baseMessage struct {
Header MessageHeader
Type MessageType
}
func (b *baseMessage) GetHeader() MessageHeader {
return b.Header
}
func (b *baseMessage) GetType() MessageType {
return b.Type
}
// your three types, with an embedded "baseMessage" part :
type QueryMessage struct {
baseMessage
Query SqlQuery
}
type UpdateMessage struct {
baseMessage
OldData map[string]interface{}
NewData map[string]interface{}
}
type InsertMessage struct {
baseMessage
Data map[string]interface{}
}
// compile time check : all the above types implement the Message interface :
var _ Message = &QueryMessage{}
var _ Message = &UpdateMessage{}
var _ Message = &InsertMessage{}
答案2
得分: 0
只是一个观点,考虑到Header
和Type
只是字段/数据而不是方法/行为,我会放弃使用Message interface
,而是使用Message struct
。
type Message struct {
Header MessageHeader
Type MessageType
Details interface{}
}
然后根据Type
字段的值,你可以将Details
转换为任何类型。
if m.Type == QueryMessageType {
q := m.Details.(SqlQuery)
} else if m.Type == UpdateMessageType {
u := m.Details.(UpdateMessageDetails)
}
或者使用类型切换(type switch)。
switch m.Details.(type) {
case SqlQuery:
// ...
case UpdateMessageDetails:
// ...
}
英文:
Just an opinion, but considering the fact that Header
and Type
are just fields/data and not methods/behaviour, I'd lose the Message interface
and instead use a Message struct
.
type Message struct {
Header MessageHeader
Type MessageType
Details interface{}
}
And then based on the value of the Type
field you can cast Details
to whatever.
if m.Type == QueryMessageType {
q := m.Details.(SqlQuery)
} else if m.Type == UpdateMessageType {
u := m.Details.(UpdateMessageDetails)
}
Or just use a type switch
switch m.Details.(type) {
case SqlQuery:
// ...
case UpdateMessageDetails:
// ...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论