在Go语言中,没有反射的情况下,最佳的动态检查模式是什么?

huangapple go评论79阅读模式
英文:

Best patterns for dynamic checks in Go without reflection?

问题

我们的代码库中是否有任何好的模式可以帮助减少对类型字段/结构的冗余检查?我觉得Lisp在处理这些问题上做得很好,但我想了解是否有任何好的模式可以不需要像反射等东西。

例如,对于自定义结构,我发现自己经常需要做很多这样的检查:

type helloworld struct {
 field1 *string
 field2 *string
 field3 int
}
if field1 != nil { 
  if len([]rune(*field1)) > MAX_ALLOWED_LEN) return errors.New("超过最大允许长度")
}
if field2 != nil { 
  if len([]rune(*field2)) > MAX_ALLOWED_LEN) return errors.New("超过最大允许长度")
}
if field3 != 0 { //做一些操作}
....

映射是很好的,但是类型有时太泛化,这也可能导致混淆。我之所以问这个问题,是因为其中一些检查是冗余的,并且在许多包中广泛存在,所以当我需要添加一个额外的字段时,重构是耗时的。如果我可以在某些类型或字段上进行验证,并保持高性能,那将很好。

英文:

Any chance there are good patterns in any of our codebases that help reduce redundant checks on typed fields/structs? I feel like Lisp handles these things well, but trying to understand if there are any good patterns without the need for something like reflection, etc.

For example, for custom structs, I find myself needing to do a lot of:

type helloworld struct {
 field1 *string
 field2 *string
 field3 int
}
if field1 != nil { 
  if len([]rune(*field1)) > MAX_ALLOWED_LEN) return errors.New("exceeded max allowable length")
}
if field2 != nil { 
  if len([]rune(*field2)) > MAX_ALLOWED_LEN) return errors.New("exceeded max allowable length")
}
if field3 != 0 { //do stuff}
....

maps are great, but typing can be too generic a lot of the time, which can also cause confusion. I ask, because some of these checks are redundant and proliferate through a lot of packages, so when I need to add an additional field, refactoring is time consuming. It would be nice if I have validation on certain types or fields while keeping performance high.

答案1

得分: 1

3个方向,不是最直接的,但它们可以减少你的检查块:

方法1:定义自己的注解

Golang在结构体中有注解

type bla struct {
  t string `json:"some,omitempty"`
}

这实际上是一个开放的注解结构(标签)。

一个很好的例子(包含一些反射)是https://github.com/mitchellh/mapstructure

看一下mapstructure.go文件的第374行。它非常可重用,你可以将其作为自己注解的基础。

要在你的上下文中使用它,你可以创建类型,然后进行解析。

方法2:DRY检查函数

你的检查似乎是相同或相似的。

一个通用的比较器,遍历字段:

func check(fields ...interface{}) error {
  for v := range fields {
    if v == nil {
      return errors.New("nil")
    }
    switch v.(type) {
      case *string:
        if len([]rune(*v)) > MAX_ALLOWED_LEN {
          return errors.New("超过最大允许长度")
        }
      }
      ...
    }
}

方法3:泛型。

方法2中的switch类型可能可以以泛型风格重写。不容易,但可能更漂亮。

英文:

3 directions, not the most straight forward, but they can reduce your check blocks:

Method 1: Define your own annotation

Golang has annotations in structs

type bla struct {
  t string `json:"some,omitempty"`
}

This is actually an open annotation structure (tags).

A nice example (contains some reflection) is https://github.com/mitchellh/mapstructure

Look at line 374 of the mapstructure.go file. It is pretty re-usable, and you could use it as base for your own annotation.

To use it in your context you would create types which you can then parse.

Method 2: DRY check function

Your check seems to be the same or similar.

A generic comparator which iterates over fields:

func check(fields ...interface{}) error {
  for v:=range fields {
    if v==nil {
      return errors.New("nil")
    }
    switch v.(type) {
      case *string:
        if len([]rune(*v)) > MAX_ALLOWED_LEN) {
          return errors.New("exceeded max allowable length")
        }
      }
      ...
    }
}

Method 3: Generics.

The switch type in method 2 might be able to be rewritten in a generic style. Not easier, but can be prettier.

huangapple
  • 本文由 发表于 2022年8月11日 07:25:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/73313745.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定