简体   繁体   English

如何检查具体类型由什么嵌入类型组成?

[英]How can I check what embedded type a concrete type is composed of?

I suspect I am trying to shoehorn go into behaving in an OOP way, but I don't know the go idiom to do what I want.我怀疑我正试图以 OOP 方式进行行为,但我不知道 go 成语来做我想做的事。

I have a Message struct that I use to pass data around in a client-server application:我有一个 Message 结构,用于在客户端-服务器应用程序中传递数据:

type Message struct {
    ID   string      `json:"id,omitempty"`
    Type string      `json:"type"`
    Data interface{} `json:"data"`
}

the Data here can be different things, for example a number of Commands:这里的数据可以是不同的东西,例如一些命令:

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"`

}

What I want to do is to check that the message data type is a Command and perform actions that are common to all commands, for example authorisation, all in one place and not having to type assert what type of command, calling the appropriate handler function and then do the auth, as this would result in massive code duplication.我想要做的是检查消息数据类型是否为 Command 并执行所有命令通用的操作,例如授权,全部在一个地方,而不必键入断言什么类型的命令,调用适当的处理函数然后进行身份验证,因为这会导致大量代码重复。

The code below reflects what I would like to happen.下面的代码反映了我想要发生的事情。

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

How do I solve this go idiomatically?我如何惯用地解决这个问题?

You can define common command stuff in interface您可以在界面中定义常用命令内容

type Commander interface{
    DoCommonStuff()
}

implement it for Command struct为命令结构实现它

func (c Command) DoCommonStuff(){
//do stuff
}

and then assert然后断言

if c, ok := m.Data.(Commander); ok {
    c.DoCommonStuff()
}

your other code should work unchanged你的其他代码应该不变

One approach is using reflection to extract common field value from the Data .一种方法是使用反射从Data提取公共字段值。 In your example, since all Command has User field, we can use it to identify whether Message.Data is a command or not.在您的示例中,由于所有Command都有User字段,我们可以使用它来识别Message.Data是否是命令。 If Command is not embedded to the data, simply return nil .如果Command没有嵌入到数据中,只需返回nil Example code:示例代码:

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM