简介
(低能预警:这是一篇毫无技术内容的日常)
ezorm https://github.com/ezbuy/ezorm是我司内部的orm工具,日常用来生成mongo、mysql、sql server框架代码,省了很多事情。其中我用的最多的就是mongo代码的生成。这个工具的核心,一个是生成原理就是渲染模板文件,一个关于驱动,就是万年好死不死的mgo(连mongo官方工具集用go写的那套都是这个库,也没什么好办法)。
顺便吐槽一下,我之前也做过类似将mgo的常用代码规范化的事情,但是为啥不做orm工具咧,因为上一家公司的项目内部,有精确规划mongo连接的需求,需要对mgo的使用定制化,比如几条连接到主库,几条连接到从库等等,普适框架无法概括。但是做电商的业务,mongo没有位于热点数据的话,不会有这个需求。
ezorm的日常使用
ezorm生成的代码日常使用是这样的
users , err := ezorm.UserMgr.find(query)
user, err := ezorm.UserMgr.findOneBy{unique index}(unique index)
这样的代码,正常人都会这么写,其实恶心的还是golang
因为项目中代码,日常是这样的
users , err := ezorm.UserMgr.find(query)
for _, user := range users {
retArry = append(retArray, doUser(user))
...
}
for … range 写多了,不管你无不无聊,反正我觉得我想吐
golang没有泛型,但是有代码生成就可以
for … range 写吐这个想法,当然不止我有,翁伟同学(ezbuy CTO)早就有了,所以他对常用类型搞了一个库去支持消除for循环的append(库忘了在哪,反正我赶脚没啥用)。因为golang不支持泛型,所有用处有限。但是orm就可以嘛,反正你的代码是生成的,跟编译期模板展开也没啥区别。
比如这样ForEach,例子是转换int64数组到int32数组
一般golang程序员:
var i64Array []int64
var i32Array []int32
for _, i64 := range {
i32Array = append(i32Array, int32(i64))
}
这个代码也没什么可说的。
吐过的golang程序员
type []int64 I64ArrayIter
func (this *I64ArrayIter) ForEach(f func (int,int64)) {
for i, i64 := range this {
f(i, i64)
}
}
var i64Array []int64
var i32Array []int32
iter := I64ArrayIter(i64Array)
iter.ForEach(func (i int, i64 int64){
i32Array = append(i32Array, int32(i64))
})
(是不是看上去酷炫了一点!)
(老师,还有更给力的吗?)
(有!老师学过FP!)
吐出过屎的golang程序员
type []int64 I64ArrayIter
func (this I64ArrayIter) ForEach(f func (int,int64)) {
for i, i64 := range this {
f(i, i64)
}
}
type []int32 I32ArrayIter
func (this I32ArrayIter) Append(i32 int32) I32ArrayIter{
this = append(this, i32)
}
var i64Array []int64
var i32Array []int32
iter64 := I64ArrayIter(i64Array)
iter32 := I32ArrayIter(i32Array)
iter.ForEach(func (i int, i64 int64){
iter32.Append(int32(i64))
})
(这货是伪代码,不要跑;这货是伪代码,不要跑;这货是伪代码,不要跑)
(因为这样append是没有用的,我只是懒得写了)
(但是讲道理这样的代码是不是酷炫了很多捏?)
其实这样的写法,就是泛型展开而已,来看上面的抽象模板
type []{Type} {Type}ArrayIter
func (this {Type}ArrayIter) ForEach(f func (int, {Type})) {
for i, one := range {Type}ArrayIter {
f(i, one)
}
}
func (this {Type}ArrayIter) Append(data {Type}) {Type}ArrayIter {
{Type}ArrayIter = append({Type}ArrayIter, data)
return {Type}ArrayIter
}
同理,这只是示意,这样append是没有任何卵用的。
那么皮一下ezorm
我在我的ezorm fork中加入了这么一个模板文件。
内容就不贴了。
其中,能让append生效的定义是
type {.Name}Iter struct {
data []*{.Name}
}
实现了我觉得比较有用的ForEach, Append,Filter,Len等FP常用的函数
生成的代码大约可以做到如下鬼畜:
blogs := []*Blog{
BlogMgr.NewBlog(),
BlogMgr.NewBlog(),
BlogMgr.NewBlog(),
BlogMgr.NewBlog(),
}
iter := NewBlogIter(blogs)
filIndex := func(i int) bool { return i%2 == 0 }
fileId := func(b *Blog) bool { return b.ID.Counter()%2 == 0 }
foreach := func(i int, b *Blog) {}
length := iter.Append(BlogMgr.NewBlog()).Foreach(foreach).
Append(BlogMgr.NewBlog()).Foreach(foreach).
Append(BlogMgr.NewBlog()).Foreach(foreach).
Append(BlogMgr.NewBlog()).Foreach(foreach).
Append(BlogMgr.NewBlog()).Foreach(foreach).
FilterByIndex(filIndex).Foreach(foreach).
Filter(fileId).Foreach(foreach).
Len()
t.Log(length)
(猫头妙啊.jpg)
用例文件在这里
总结
一直想给golang做一个支持泛型的脚本语言,但是由于各种原因(主要是不会)没做。然后发现在基于模板的代码生成工具里面,做到一定程序的泛型其实很容易哟。咻咪!(逃)。
当然这个patch,我是不会尝试合并到主分支的。个人趣味而已,对工具的价值没有任何用处。