Set模式解决零值问题

24 阅读2分钟
http://127.0.0.1:8080/users

{
    "Code": 200,
    "Message": "OK",
    "Success": true,
    "Data": {
        "Mobile": {
          "Value": ""
        },
        "Age": {
          "Value": 0
        },
        "Level": {
          "Value": 0
        },
        "Nickname": {
          "Value": ""
        }
    }
}

当我们请求用户列表接口时,参数都不传,Mobile、Age、Nickname字段为空我们知道查询数据库的时候不需要使用,因为默认零值在这三个字段的场景下时非法的,但是Level字段的零值是合法的,那么我们是使用还是不使用呢?

这就是臭名昭著的零值问题。

零值问题在增量更新中也广泛存在,例如更新用户昵称:

{
  "Nickname": "John"
}
type User struct {
    ID int
    Mobile string
    Age     int
    Nickname string
    Level int
}
user {Nickname: John}

我们直接使用user去更新数据库,会导致除Nickname字段其他所有的字段都会更新为空。

两种方案解决零值问题:

1、使用指针
缺点:无处不在的nil判断,操作起来会非常繁琐

2、客户端指定更新字段 JSON Merge Patch && JSON Patch
缺点:客户端和服务器端同时增加开发成本

有没有更好的解决方案呢?

type Nickname struct {
    Value string `binding:"min=2,max=10"`
    Set   bool
}

func (o *Nickname) SetTo(v string) {
    o.Set = true
    o.Value = v
}

func (o *Nickname) UnmarshalJSON(data []byte) error {
    if data[0] != '{' {
        o.SetTo(gjson.ParseBytes(data).String())
        return nil
    }

    result := gjson.GetBytes(data, "Value")
    if result.Exists() {
        o.SetTo(result.String())
    }
    return nil
}

不管是使用指针还是指定更新字段,目的都是为了标识某一个字段是否是有效数据。

我们可以在属性对象中增加Set变量,用来标识字段是否有效,然后在UnmarshalJSON函数中解析数据的同时设置Set变量,就这是Set模式。

Set模式优势:
1、杜绝了指针方案的各种繁琐操作,同时避免了nil异常
2、客户端无痕对接,降低了对接成本

那么后续又该如何使用Set模式呢,请看下文分解。

源码链接