我正在Go中实现一个消息传递系统。因此,我有一个称为Msg
的常规接口。 Msg
界面定义了许多公共字段,例如源,目的地,发送时间,接收时间等。我无法定义Msg
的完整列表,因为我希望库用户定义具体的类型。 Msg
个。
要提供具体的Msg
类型,用户将需要实现大量的getter和setter列表,这很烦人。
我尝试的一种解决方案是提供一个简单的基类,例如MsgBase
,并定义所有常见的属性以及getter和setter。对于Msg
的每种具体类型,我都嵌入了一个指向MsgBase
的指针。此解决方案有效。
但是,我想在具体的MsgBase
类型中嵌入Msg
的值版本。这是因为这样的Msg
在执行中创建了太多次,并且动态分配MsgBase
会增加垃圾回收的开销。我真的希望所有Msg
都是静态分配的,因为它们是由组件传递的,并且永远不应该共享。如果使用MsgBase
的值版本,则不能使用MsgBase
中定义的设置器。
我想知道是否有解决此问题的简单方法?
编辑:添加示例代码
type Msg interface {
// Agent is another interface
Src() Agent
SetSrc(a Agent)
Dst() Agent
SetDst(a Agent)
... // A large number of properties
}
type MsgBase struct {
src, dst Agent
... // Properties as private fields.
}
func (m MsgBase) Src() Agent {
return m.src
}
func (m *MsgBase) SetSrc(a Agent) {
m.src = a
}
... // Many other setters and getters for MsgBase
type SampleMsg struct {
MsgBase // option1
*MsgBase // option2
}
答案 0 :(得分:2)
请记住,Go不像Java一样具有面向对象的继承。听起来您正在尝试编写一个抽象的基类,该基类封装了“消息”的所有部分。那不是真正的Go风格。
您描述的字段是典型的邮件元数据。您可以将此元数据封装在纯数据结构中。它不一定需要任何行为,也不一定需要getter和setter方法。
type MessageMeta struct {
Source Agent
Destination Agent
}
更面向对象的方法是说一条消息具有一个(可变的)元数据块和一个(不变的,编码的)有效载荷。
import "encoding"
type Message interface {
encoding.BinaryMarshaler // requires MarshalBinary()
Meta() *MessageMeta
}
type SomeMessage struct {
MessageMeta
Greeting string
}
func (m *SomeMessage) Meta() *MessageMeta {
return &m.MessageMeta
}
func (m *SomeMessage) MarshalBinary() ([]byte, error) {
return []byte(m.Greeting), nil
}
一种更程序化的方法可以分开传递这两件事也是合理的。在这种情况下,“消息”没有接口,您只需传递编码的有效负载即可。像encoding.BinaryMarshaler
这样的标准库接口在这里可能很有意义。您可以将其包含在库的下级界面中。
func Deliver(meta *MessageMeta, payload []byte) error { ... }
彼此轻松翻译
func DeliverMessage(m Message) error {
payload, err := m.Payload()
if err != nil {
return err
}
meta := m.Meta()
return Deliver(meta, payload)
}
如果其中一个元数据字段是“传递给”的,请确保在整个链中始终将指针传递给元数据对象,以便您可以在原始对象中更新该字段。
我不会担心将垃圾收集作为头等大事,除非避免过度浪费,并且如果GC开始显示在配置文件中,则检查对象分配。在这里创建两个对象而不是一个对象可能不会成为一个大问题。