如何检查具体类型由哪些嵌入类型组成?
How can I check what embedded type a concrete type is composed of?
我怀疑我是在试图用鞋拔来以 OOP 方式表现,但我不知道用 go 惯用语来做我想做的事。
我有一个消息结构,用于在客户端-服务器应用程序中传递数据:
type Message struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Data interface{} `json:"data"`
}
这里的数据可以是不同的东西,例如一些命令:
type Command struct {
User *types.UserInfo `json:"user"`
}
type CommandA struct {
Command
A *AData `json:"a_data"`
}
type CommandB struct {
CommandB
B *BData `json:"b_data"`
}
我想做的是检查消息数据类型是否为命令并执行所有命令共有的操作,例如授权,所有这些都在一个地方,而不必键入断言命令的类型,调用适当的处理函数然后进行身份验证,因为这会导致大量代码重复。
下面的代码反映了我希望发生的事情。
for {
select {
case m := <-in:
// what I would like to do, obviously not working as
// m.Data is not of type Command but the actual command type
if c, ok := m.Data.(msg.Command); ok {
// do auth and other common stuff
}
switch t := m.Data.(type) {
case *msg.CommandA:
go srv.handleCommandA(m.ID, t)
case *msg.CommandB:
go srv.handleCommandB(m.ID, t)
// etc etc
default:
// do something
}
}
}
我该如何以惯用的方式解决这个问题?
一种方法是使用反射从 Data
中提取公共字段值。在您的示例中,由于所有 Command
都有 User
字段,我们可以使用它来识别 Message.Data
是否是命令。如果 Command
未嵌入到数据中,则只需 return nil
。示例代码:
func GetUserInfo(v interface{}) *types.UserInfo {
vt := reflect.ValueOf(v)
if vt.Kind() == reflect.Ptr {
vt = vt.Elem()
}
if vt.Kind() != reflect.Struct {
return nil
}
u := vt.FieldByName("User")
if !u.IsValid() {
return nil
}
user, ok := u.Interface().(*types.UserInfo)
if !ok {
return nil
}
return user
}
//Call GetUserInfo then perform common operation
user := GetUserInfo(m.Data)
if user != nil {
//Do auth and other common stuff
}
您可以在界面中定义常用的命令内容
type Commander interface{
DoCommonStuff()
}
为命令结构实现它
func (c Command) DoCommonStuff(){
//do stuff
}
然后断言
if c, ok := m.Data.(Commander); ok {
c.DoCommonStuff()
}
您的其他代码应该可以正常工作
我怀疑我是在试图用鞋拔来以 OOP 方式表现,但我不知道用 go 惯用语来做我想做的事。
我有一个消息结构,用于在客户端-服务器应用程序中传递数据:
type Message struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Data interface{} `json:"data"`
}
这里的数据可以是不同的东西,例如一些命令:
type Command struct {
User *types.UserInfo `json:"user"`
}
type CommandA struct {
Command
A *AData `json:"a_data"`
}
type CommandB struct {
CommandB
B *BData `json:"b_data"`
}
我想做的是检查消息数据类型是否为命令并执行所有命令共有的操作,例如授权,所有这些都在一个地方,而不必键入断言命令的类型,调用适当的处理函数然后进行身份验证,因为这会导致大量代码重复。
下面的代码反映了我希望发生的事情。
for {
select {
case m := <-in:
// what I would like to do, obviously not working as
// m.Data is not of type Command but the actual command type
if c, ok := m.Data.(msg.Command); ok {
// do auth and other common stuff
}
switch t := m.Data.(type) {
case *msg.CommandA:
go srv.handleCommandA(m.ID, t)
case *msg.CommandB:
go srv.handleCommandB(m.ID, t)
// etc etc
default:
// do something
}
}
}
我该如何以惯用的方式解决这个问题?
一种方法是使用反射从 Data
中提取公共字段值。在您的示例中,由于所有 Command
都有 User
字段,我们可以使用它来识别 Message.Data
是否是命令。如果 Command
未嵌入到数据中,则只需 return nil
。示例代码:
func GetUserInfo(v interface{}) *types.UserInfo {
vt := reflect.ValueOf(v)
if vt.Kind() == reflect.Ptr {
vt = vt.Elem()
}
if vt.Kind() != reflect.Struct {
return nil
}
u := vt.FieldByName("User")
if !u.IsValid() {
return nil
}
user, ok := u.Interface().(*types.UserInfo)
if !ok {
return nil
}
return user
}
//Call GetUserInfo then perform common operation
user := GetUserInfo(m.Data)
if user != nil {
//Do auth and other common stuff
}
您可以在界面中定义常用的命令内容
type Commander interface{
DoCommonStuff()
}
为命令结构实现它
func (c Command) DoCommonStuff(){
//do stuff
}
然后断言
if c, ok := m.Data.(Commander); ok {
c.DoCommonStuff()
}
您的其他代码应该可以正常工作