前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang实现动态调用不同struct中不同的方法

golang实现动态调用不同struct中不同的方法

作者头像
公众号-利志分享
发布2022-04-25 09:12:24
1.6K0
发布2022-04-25 09:12:24
举报
文章被收录于专栏:利志分享

在我们的业务中,尤其涉及到后台业务,在我们不用考虑性能的情况下,我们写后台框架的时候,可能会遇到这样的一些情况,如何通过某些struct名和方法名传递进来执行不同的逻辑。这个时候我想的是go的反射是最好的实现这种功能,当然在go里面也可以通过定义配置来实现进入动态进入不同的struct名和方法名,或者其他方式(如果你有更好的方式,可以互相交流)。

下面我们来讲一个例子

假如前端传递UserController和GetName,后端通过UserController和GetName调用struct为UserController的GetName的方法。这样前端能拿到用户的名称。我想的是如果前端传PermissionController和GetPermission等其他不同的struct中不同的方法我都能动态的执行不同的方法,当然如果找不到对应的struct和不同的方法,那肯定是需要告诉前端你请求的方法不存在。下面我们来实现这样的一个功能。

首先定义struct,用一个map来管理struct

代码语言:javascript
复制
//这个是注册好的struct
var registerFunc = map[string]interface{}{
  "UserController": &UserController{},
}

type UserController struct {
}

func (u *UserController) GetName(param map[string]string) *ResData {
  ret := ResData{}
  ret.Code = 10000
  ret.Msg = "succ"
  if param["id"] == "1" {
    ret.Data = "追麾"
  }
  return &ret
}

type PermissionController struct {
}

func (p *PermissionController) GetPermission() *ResData {
  ret := ResData{}
  return &ret
}

上面是基础类,下面我们用代码来实现。我直接在代码中进行解释,解析直接看代码的注释。

代码语言:javascript
复制
func main() {
  //假如我们传递的参数是params,这里我的value用string了,实际应用中得用interface
  params := map[string]string{
    "controller": "UserController",
    "func":       "GetName",
    "id":         "1",
  }
  //判断方法是否存在
  if _, ok := registerFunc[params["controller"]]; !ok {
    fmt.Println("方法不存在")
    return
  }
  c := reflect.ValueOf(registerFunc[params["controller"]])
  callback := c.MethodByName(params["func"])
  //判断调用的方法是否有效
  if !callback.IsValid() {
    fmt.Println("方法名不存在")
    return
  }
  //这个方法是通过callback调用,为什么要这么写,因为call方法只支持传递slice,
  res := toCombineArr(callback, params)
  lastData := ResData{}
  //下面这里是通过反射拿调用的方法的返回值。
  for _, item := range res {
    bin, err := json.Marshal(item.Interface())
    if err != nil {
      fmt.Println(err)
      return
    }
    err = json.Unmarshal(bin, &lastData)
    if err != nil {
      fmt.Println(err)

    }
    break
  }
  fmt.Println("lastData", lastData)
}

func toCombineArr(callback reflect.Value, params ...interface{}) []reflect.Value {
  //把传进来的参数组装成relect.Value,然后调用call方法
  in := make([]reflect.Value, len(params))
  for k, param := range params {
    in[k] = reflect.ValueOf(param)
  }
  d := callback.Call(in)
  if callback.IsZero() {
    fmt.Println("error callback")
    return nil
  }
  return d
}

最后我们整理一下上面的代码:执行结果如下:

代码语言:javascript
复制
lastData {10000 succ 追麾}

下面是整理的代码,我这里再贴一下:

代码语言:javascript
复制
package main

import (
  "fmt"
  jsoniter "github.com/json-iterator/go"
  "reflect"
)

var json = jsoniter.ConfigCompatibleWithStandardLibrary

func main() {
  //假如我们传递的参数是params,这里我的value用string了,实际应用中得用interface
  params := map[string]string{
    "controller": "UserController",
    "func":       "GetName",
    "id":         "1",
  }
  //判断方法是否存在
  if _, ok := registerFunc[params["controller"]]; !ok {
    fmt.Println("方法不存在")
    return
  }
  c := reflect.ValueOf(registerFunc[params["controller"]])
  callback := c.MethodByName(params["func"])
  //判断调用的方法是否有效
  if !callback.IsValid() {
    fmt.Println("方法名不存在")
    return
  }
  //这个方法是通过callback调用,为什么要这么写,因为call方法只支持传递slice,
  res := toCombineArr(callback, params)
  lastData := ResData{}
  //下面这里是通过反射拿调用的方法的返回值。
  for _, item := range res {
    bin, err := json.Marshal(item.Interface())
    if err != nil {
      fmt.Println(err)
      return
    }
    err = json.Unmarshal(bin, &lastData)
    if err != nil {
      fmt.Println(err)

    }
    break
  }
  fmt.Println("lastData", lastData)
}

func toCombineArr(callback reflect.Value, params ...interface{}) []reflect.Value {
  //把传进来的参数组装成relect.Value,然后调用call方法
  in := make([]reflect.Value, len(params))
  for k, param := range params {
    in[k] = reflect.ValueOf(param)
  }
  d := callback.Call(in)
  if callback.IsZero() {
    fmt.Println("error callback")
    return nil
  }
  return d
}

//这个是注册好的struct
var registerFunc = map[string]interface{}{
  "UserController": &UserController{},
}

type UserController struct {
}

func (u *UserController) GetName(param map[string]string) *ResData {
  ret := ResData{}
  ret.Code = 10000
  ret.Msg = "succ"
  if param["id"] == "1" {
    ret.Data = "追麾"
  }
  return &ret
}

type PermissionController struct {
}

func (p *PermissionController) GetPermission() *ResData {
  ret := ResData{}
  return &ret
}

type ResData struct {
  Code int32       `json:"code"`
  Msg  string      `json:"msg"`
  Data interface{} `json:"data"`
}

上面我们通过struct名和方法动态调用,在我的实践中,出现一个问题,假如我定义一个确定好的struct作为返回参数,struct的参数顺序是从上到下返回,但是通过反射之后返回的struct的结果顺序是无序,反射之后会把struct转换成一个map,造成了返回的无序性。具体问题如下。

我改了GetName的方法

代码语言:javascript
复制
func (u *UserController) GetName(param map[string]string) *ResData {
  ret := ResData{}
  ret.Code = 10000
  ret.Msg = "succ"
  if param["id"] == "1" {
    ret.Data = "追麾"
  } else {
    tStr := `{"活动id": "sas", "付费": "gdf", "活动号": "66", "A4": "88", "A5": "333", "A6": "ggg", "A7": "ggg"}`
    tTmpJson := TmpJson{}
    err := json.Unmarshal([]byte(tStr), &tTmpJson)
    if err != nil {
      fmt.Println("err", err)
      return nil
    }
    fmt.Println("tTmpJson:", tTmpJson)
    ret.Data = tTmpJson
  }
  return &ret
}

增加一个TmpJson的struct结构:

代码语言:javascript
复制
type TmpJson struct {
  D活动Id string `json:"活动_id"`
  E付费   string `json:"付费"`
  G活动号  string `json:"活动号"`
  A4    string `json:"A4"`
  A5    string `json:"A5"`
  A6    string `json:"A6"`
  A7    string `json:"A7"`
}

然后请求参数改成如下:

代码语言:javascript
复制
params := map[string]string{
  "controller": "UserController",
  "func":       "GetName",
  "id":         "2",
}

最后返回结果如下:

代码语言:javascript
复制
tTmpJson:{ gdf 66 88 333 ggg ggg}
lastData {10000 succ map[A4:88 A5:333 A6:ggg A7:ggg 付费:gdf 活动_id: 活动号:66]}

反射给静态语言提供很好的便利,但是其中的某些坑还是很多,好了关于动态调用不同struct的不同的方法就到这里,有兴趣的可以找我交流。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 利志分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档