Loading... # 一、前言 go中所有类型都有默认值,比如string默认为"",int默认为0。而有时候空string 或者int的0值是有业务含义的,那么如何识别一个字段的空值还是默认的,还是开发者设置的空那? # 二、问题展示 假设客户端要传输的body内容为如下结构: ```go type Person struct { Name string Age int Address string } person := &Person{Name: "加多", Address: ""} bb, err := json.Marshal(person) ``` 那么客户端把消息内容序列化为二进制后,通过网络传输到服务端后,服务端以上面结构反序列化: ```makefile p := Person{} json.Unmarshal([]byte(bb), &p) fmt.Println(p) ``` 然后会发现p.Age ==0 并且p.Address == "",那么服务端就不知道到底是客户端业务需要设置为Age为0,还是客户端没有设置,而是因为默认值才0的。 # 三、问题解决 重新定义Person的结构体,使用指针类型变量: ```go type Person struct { Name *string Age *int Address *string } name := "加多" person := &Person{Name: &name} bb, err := json.Marshal(person) ``` 那么客户端把消息内容序列化为二进制后,通过网络传输到服务端后,服务端以上面结构反序列化: ```makefile p := Person{} json.Unmarshal([]byte(bb), &p) fmt.Println(p) ``` 由于变量是指针类型,所以服务端可以进行判断: ```objectivec if (p.Age == nil){ fmt.Println("客户端没有设置Age") }else { fmt.Println("客户端设置Age:=",p.Age) } ``` 如上服务端可以识别客户端到底是否设置了值,但是客户端编写代码时,由于变量都是指针类型,所以必须先单个创建变量,然后再&取其地址,这个比较繁琐。其实客户端开发者关心的是string类型,而不是*string,这造成开发者理解和开发负担。 那么有办法让客户端开发时传入的还是非指针类型的变量,但是客户端内部还是可以区分客户端到底是否设置值了? 答案是肯定的,我们只需要使用Builder模式来为Person结构体添加一个构造器: ```go func NewPersonBuilder() *PersonBuilder { return &PersonBuilder{} } type PersonBuilder struct { name string nameFlag bool age int ageFlag bool address string addressFlag bool } func (builder * PersonBuilder) Name(name string) * PersonBuilder { builder.name = name builder.nameFlag = true return builder } func (builder * PersonBuilder) Age(age int) * PersonBuilder { builder.age = age builder.ageFlag = true return builder } func (builder * PersonBuilder) Address(addr string) * PersonBuilder { builder.address = addr builder.addressFlag = true return builder } func (builder * PersonBuilder) Build() * Person { person := &Person{} if builder.nameFlag { person.Name = &builder.name } if builder.addressFlag { person.Address = &builder.address } if builder.ageFlag { person.Age = &builder.age } return person } ``` 然后使用时: ```makefile person := NewPersonBuilder(). Name("加多"). Address(""). Build() bb, err := json.Marshal(person) ``` 然后服务端就知道Address是客户端故意设置的为"",Age是默认为0 # 四、总结 当问题得不到解决时,往往加一层就可以解决。本文场景,如果客户端使用了SDK调用,并且SDK是自动生成的,那么Builder这一层是不需要手写的,根本不需要额外的开发成本。 最后修改:2022 年 09 月 11 日 © 允许规范转载 打赏 赞赏作者 微信 赞 1 如果觉得我的文章对你有用,请随意赞赏