一、前言
go中所有类型都有默认值,比如string默认为"",int默认为0。而有时候空string 或者int的0值是有业务含义的,那么如何识别一个字段的空值还是默认的,还是开发者设置的空那?
二、问题展示
假设客户端要传输的body内容为如下结构:
type Person struct {
Name string
Age int
Address string
}
person := &Person{Name: "加多", Address: ""}
bb, err := json.Marshal(person)
那么客户端把消息内容序列化为二进制后,通过网络传输到服务端后,服务端以上面结构反序列化:
p := Person{}
json.Unmarshal([]byte(bb), &p)
fmt.Println(p)
然后会发现p.Age ==0 并且p.Address == "",那么服务端就不知道到底是客户端业务需要设置为Age为0,还是客户端没有设置,而是因为默认值才0的。
三、问题解决
重新定义Person的结构体,使用指针类型变量:
type Person struct {
Name *string
Age *int
Address *string
}
name := "加多"
person := &Person{Name: &name}
bb, err := json.Marshal(person)
那么客户端把消息内容序列化为二进制后,通过网络传输到服务端后,服务端以上面结构反序列化:
p := Person{}
json.Unmarshal([]byte(bb), &p)
fmt.Println(p)
由于变量是指针类型,所以服务端可以进行判断:
if (p.Age == nil){
fmt.Println("客户端没有设置Age")
}else {
fmt.Println("客户端设置Age:=",p.Age)
}
如上服务端可以识别客户端到底是否设置了值,但是客户端编写代码时,由于变量都是指针类型,所以必须先单个创建变量,然后再&取其地址,这个比较繁琐。其实客户端开发者关心的是string类型,而不是*string,这造成开发者理解和开发负担。
那么有办法让客户端开发时传入的还是非指针类型的变量,但是客户端内部还是可以区分客户端到底是否设置值了?
答案是肯定的,我们只需要使用Builder模式来为Person结构体添加一个构造器:
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
}
然后使用时:
person := NewPersonBuilder().
Name("加多").
Address("").
Build()
bb, err := json.Marshal(person)
然后服务端就知道Address是客户端故意设置的为"",Age是默认为0
四、总结
当问题得不到解决时,往往加一层就可以解决。本文场景,如果客户端使用了SDK调用,并且SDK是自动生成的,那么Builder这一层是不需要手写的,根本不需要额外的开发成本。
戳下面阅读
👇
golang并发教程 ForkJoinPool K8s网络模型
点亮再看哦👇