今天和一个同事一起review代码,看他又写用embedded struct抽公共代码,稍微吐槽了一下。唠唠叨叨,骂骂咧咧地bb了好久,反正他也没听进去。
刚开始写go那会儿,我也傻傻的照着网上的《用内嵌实现继承的方法》这类博客去写公共代码。但有句话叫go语言没有继承(Go has no inheritance.)也不是说go不需要继承,过载,面向对象,阿巴阿巴。就是现在的项目就是go写的,打工人写就完事儿了。但想想go就是tmd没有继承!
有些代码有点重复写就写了,这样好改啊(雅言叫可维护性强)
看这么个例子, 比与A和B都有Fixed,给抽它个公共struct。
type ProductA struct {
Embedded
Flexible int `json:"flexible"`
}
type ProductB struct {
Embedded
Flexible string `json:"flexible"`
}
type Embedded struct {
Fixed int `json:"fixed" validate:"required,gte=10,lte=20`
}
// 随便写个方法
func (e Embedded) add() int {
return e.Fixed + 1
}
写的挺好的公共类, 但有一天业务变了。A里面的校验10到20不变,B改了改成10到30了。除了把Fixed分别写道A和B,还可以把validate tag拿掉,写个类方法简单校验一下。
但设想一下真实的业务场景,Embedded其实挺复杂的,有好些field在里面。有好些类方法还用到了Fixed和其他的field。这tmd重构一下就伤筋动骨,肯定不会为了一个小校验,把Embedded移除。
还有这个让人绝望的问答,应该很多写go的新手也都想过struct tag怎么用变量吧。毕竟不是java annotation,没那么多功能。Go就是这么一个古朴的语言,就是写着写着就觉得好像应该有这么个功能吧,别的语言都有啊,但好像没见别人这么写过啊(不祥的预感)。。。woc,不能这么写。
就像有个动图,是用多光标快速给多个类型写比较大小还是啥的,讽刺go语言的copy hell。不过写go真的难免多copy。
比如写java经常搞一些Dog extends Animal
, Cat extends Animal
,写个Animal.eat()
,再写个Dog.eat()
,再super()
啥的。把it作为母类,extends出来he和she然后写it is as it is
, he is as it is
, she is as it is
。
生搬硬套到golang就不大行。搁这里要写个type Eater interface
。没那么多抽象结构,就硬写it is
, he is
, she is
简单直接
尽量别整embedded struct搞继承 多为之后改代码的人考虑(官话叫可维护性强)
再举个例子
接着刚才Fixed校验的方法,我要返回json结果体中fixed
字段不对。
func (e Embedded) validate() error {
// 校验代码
// 通过return nil
// ....
// 校验失败
return FieldError("fixed") //FieldError 返回用户错误提示信息
}
但是这么写的话,如果要修改Embedded.Fixed
的json字段名称,就有两处代码要修改,而且很容易改漏。
type Embedded struct {
Fixed int `json:"fixed_value"`
}
func (e Embedded) validate() error {
// 校验代码
// 通过return nil
// ....
// 校验失败
return FieldError("fixed") //FieldError 返回用户错误提示信息
}
就比如领导说了把fixed
改成fixed_value
,然后校验那里就改漏了,也没啥静态检查啥的。这两个值一个是struct tag的一部分,一个是代码里面的一个string常量。对于go来说他们没啥关系,没有必要在struct tag里面使用变量啊?又不是不能用,是不是?go就是这么朴素的语言。
我也想过用reflect,但简单说还还是不好用,因为Embedded.Fixed
这种语法也不存在, 只能是
e := Embedded{}
e.Fixed ...
https://stackoverflow.com/questions/29752462/using-a-variable-in-the-struct-tag
算了不骂了,睡吧 明天还要打工。