【发布时间】:2019-01-18 21:40:57
【问题描述】:
我们有以下函数:
func (h *Handler) Handle(message interface{}) error {
//here there is a switch for different messages
switch m := message.(type) {
}
}
此签名已给出且无法更改。处理程序处理大约 20 种不同的消息类型。
现在,其中一些消息(大约 4 条)需要特殊的后处理。在不同的包中。
因此,我想这样做:
func (h *Handler) Handle(message interface{}) error {
//here there is a switch for different messages
switch m := message.(type) {
}
//only post-process if original message processing succeeds
postProcessorPkg.Process(message)
}
现在,在Process 函数中,我想快速查找消息类型是否确实属于我们需要后处理的类型。我不想在这里再次发送switch。有许多处理程序,在不同的包中,具有不同数量的消息类型,并且应该是通用的。
所以我想在后处理器中注册消息类型,然后进行查找:
func (p *Postprocessor) Register(msgtype interface{}) {
registeredTypes[msgtype] = msgtype
}
然后
func (p *Postprocessor) Process(msgtype interface{}) error {
if ok := registeredTypes[msgtype]; !ok {
return errors.New("Unsupported message type")
}
prop := GetProp(registeredTypes[msgtype])
doSmthWithProp(prop)
}
现在这一切都行不通了,因为据我所知,我只能“注册”消息的实例,而不是消息类型本身。因此,地图只会匹配消息的特定实例,而不是它的类型,这正是我所需要的。
所以我想这需要重新设计。我可以完全放弃注册和地图查找,但是
- 我无法将
Handle函数更改为特定类型(签名需要保留message interface{} - 我想避免不得不使用
reflect,因为我很难与一些同事捍卫这样的解决方案。
【问题讨论】:
-
如果您想要类型映射,我认为没有办法避免使用
reflect。顺便说一句,当你制作这样一个只有键真正重要的映射时,我建议使用struct{}作为值类型,而不是使用interface{}。在 go 中,表示空值和无用值的惯用方式是struct{}{}。 -
IMO,解决您的问题的最干净的方法是在支持该类型的情况下从
Handle调用Process。你已经知道Handle中的类型,因为你已经在切换它了。 -
我还是个 Go 菜鸟,但是 "some messages ... need post-processing" 让我觉得你应该让
Postprocessor成为一个接口需要Postprocess方法并且只满足需要后处理的类型的接口。然后,您可以使用if p, ok := message.(Postprocessor); ok { p.Postprocess(...) }之类的东西来避免需要其他类型的开关。此选择还允许您自定义特定类型的后处理行为,因此您无需处理具体的“注册”类型。满足接口=注册类型进行后处理。 -
非常好的想法@ChronoKitsune!您可能应该将此作为答案发布,很确定这是解决他/她问题的好方法。
-
确实我也已经想到了@ChronoKitsune 的提议。但是在需要的每种消息类型上实现
Process方法本身是完全过分的。当然,我可以只使用“标记”界面,例如type Postprocess interface { NeedsPostprocess() bool }这在我看来有点傻,因为每个 msg 类型都会实现相同的func (c *ConcreteMsg) NeedsPostprocess() bool { return true},但我认为这是我们可以达到的最佳目标