kevinyan815/gocookbook

巧用匿名结构体

kevinyan815 opened this issue · 0 comments

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

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

// declare the 'car' struct type
type car struct {
    make    string
    model   string
    mileage int
}

// create an instance of a car
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[string]interface{}类型的变量里要好很多,json数据解析到匿名在解析的过程中可以进行类型检查会更安全,使用的时候直接通过s.FieldName访问字段也比map访问起来更方便和直观。

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

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

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"`
} 

再在Response声明里使用定义的UserCoupon了

type UserCouponResponse struct {
    Code int64  `json:"code"`
    Msg  string `json:"message"`
    Data []*UserCoupon `json:"data"`
}

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

还是那句话匿名结构体只在你觉得这还要定义个类型没必要的时候使用,用好的确实能提高点代码生产效率。