Go匿名结构体提高搬砖效率

Wednesday, December 6, 2023

提高效率体现在两方面:

  • 减少一些不会复用的类型定义
  • 节省给类型起名字的时间

通过匿名结构体名字就知道,本身没有类型名字,能节省起名字的时间,也能减少起错名字带来的误解。

具名结构体

具名结构体就是平时的普通结构体。

结构体是用于把一组字段组织在一起,在Go语言里抽象表达现实世界的事物,类似“蓝图”一样。

比如定义一个名字为car的结构体在程序里表示“小汽车” :

type car struct {
	make	string
	model	string
	mileage	int
}

用这个结构体的地方通过其名字引用其即可,比如创建上面定义的结构体实例:

newCar := car{
	make:    "Ford",
	model:   "taurus",
	mileage: 200000,
}

匿名结构体

匿名结构体顾名思义就是没有名字的结构体,通常只用于在代码中仅使用一次的结构类型,比如:

func showMyCar() {
	newCar := struct {
		make	string
		model	string
		mileage	int
	}{
		make:	"Ford",
		model:	"Taurus",
		mileage: 200000,
	}
	fmt.Printlb(newCar.mode)
}

上面函数中声明的匿名结构体赋值给了函数中的变量,所以只能在函数中使用。

若一个结构体初始化后只被使用一次,那么使用匿名结构体就很方便,不用在程序的package中定义太多的结构体类型,比如在解析接口的响应到结构体后,就可以使用匿名结构体。

用于解析接口响应

func createCarHandler(w http.ResponseWriter, req *http.Request) {
    defer req.Body.Close()
    decoder := json.NewDecoder(req.Body)
    newCar := struct {
        Make    string `json:"make"`
        Model   string `json:"model"`
        Mileage int    `json:"mileage"`
    }{}
    err := decoder.Decode(&newCar)
    if err != nil {
        log.Println(err)
        return
    }
    ......
    return
}

上面这种代码一般在控制层写,可以通过匿名结构体实例解析到请求后再去创建对应的DTO或者领域对象供服务层或者领域层使用。

为什么不把API响应解析到DTO对象里?匿名结构体的使用场景是在觉得定一个struct不值得、不方便的情况下使用。比如程序拿到接口响应后需要按业务规则加工下才能创建DTO实例这种情况,就很适合用匿名结构体先解析响应。

比map更健壮

使用匿名解析接口响应要比把响应解析到map[string]interface{}类型的变量里要好,json数据解析到匿名结构体的时候在解析的过程中会进行类型检查,更安全。使用的时候直接通过s.FieldName访问字段也比map访问起来更方便和直观。

用于定义项目约定的公共字段

除了上述结构体初始化只使用一次的情况,在项目中定义各个接口的返回或者DTO时,有的公共字段使用匿名结构体声明类型也很方便。

一般在启动项目的时候我们都会约定项目提供的接口的响应值结构,比如响应里必须包含Code、Msg、Data三个字段,每个接口会再细分定义返回的Data的结构,这个时候用匿名结构体能节省一部分编码效率。

比如下面这个 Response 的结构体类型定义:

type UserCouponResponse struct {
 Code int64  `json:"code"`
 Msg  string `json:"message"`
 Data []*struct {
  CouponId           int    `json:"couponId"`
  ProdCode           string `json:"prodCode"`
  UserId             int64  `json:"userId"`
  CouponStatus       int    `json:"couponStatus"`
  DiscountPercentage int    `json:"discount"`
 } `json:"data"`
}

就可以先省去定义一个 UserCoupon 类型。

type UserCoupon struct {
    CouponId           int    `json:"couponId"`
    ProdCode           string `json:"prodCode"`
    UserId             int64  `json:"userId"`
    CouponStatus       int    `json:"couponStatus"`
    DiscountPercentage int    `json:"discount"`
} 

当然,如果 UserCoupon 是你的项目其他地方也会用到的类型,那么先声明,顺带在 Response 结构体里也使用也是没问题的。只要会多次用到的类型都建议声明成正常的结构体类型。

匿名结构体只在自己觉得“这还要定义个类型?”时使用。

Golang修炼

Go语法快速预览

Go语言RWMutex读写互斥锁

comments powered by Disqus