Go中基础类型默认值问题与解决

d2fa3676fa9415a8370099620807e8ce.png

一、前言

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网络模型

  我的视频号  

点亮再看哦👇

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值