如何在Go中建模这些类型

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

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

它们都有两个共同的属性,HeaderType。最终,我需要将它们聚合成一个通用消息的数组。
目前,我的 Message 接口看起来是这样的:

type Message interface{}

所以,我基本上做的是这样的(将它们全部转换为 Message 接口):

q := QueryMessage{ ... }
u := UpdateMessage{ ... }
i := InsertMessage{ ... }
allMessages := [3]Message { Message(q), Message(u), Message(i) }

这样可以工作,但是它丢失了所有的类型信息,我希望仍然能够从 Message 类型中公开 HeaderType(这样客户端代码理论上可以根据 TypeMessage 强制转换回其原始类型)。

如何实现这一点?我找不到一个合适的方法,因为接口不能有属性,如果我将 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{}

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

答案2

得分: 0

只是一个观点,考虑到HeaderType只是字段/数据而不是方法/行为,我会放弃使用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:
     // ...
}

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

发表评论

匿名网友

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

确定