使用链表进行批量执行操作#
最近在项目中学到一种挺有意思的编程技巧。这里记录一下
在创建一个抽奖的时候,前端会携带许许多多的数据过来,这些数据有抽奖本身的、抽奖配置的、奖项信息的、参与方式的、助力方式的、创建人信息的、创建人联系方式的等等。
这里面每一种都需要校验,有些条件还是互斥的,有些条件的判定依赖与其他条件的结果,所以无法只靠结构体 tag 去使用 gValid 库校验,并且在验证的时候还需要考虑验证的顺序。
解决方案#
通过阅读项目的源码以后,发现是实现了一个链表,这个链表有 3 个方法:
AddToChainHead( Handler )
: 添加结点到链表的表头AddToChainTail( Handler )
: 添加结点到链表的表尾Iterator( Param Structure )
: 遍历链表
结点的数据域是一个 Handler,这个结构体必须实现 Handler 接口,在遍历链表时需要调用其校验方法。
Handler 接口中只有一个 Handle( Param Structure ) error
校验方法。
在创建抽奖的时候,初始化这个链表,将校验器逐个加入链表,最后遍历链表,在链表中调用结点的中的校验器的 Handle 校验方法执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| package chain
// Handler 接口,参数放在 req 这个结构体中
type Handler interface {
Handle(*Req) error
}
type node struct {
Data Handler // 数据域
Next *Node // 指针域
}
type chain struct {
HeadNode *Node // 头结点
TailNode *Node // 尾结点
}
// 创建链表
type NewChain() *chain {
return &chain{}
}
// 添加结点到链表头部
func (c *chain) AddToHead(h Handler) {
node := &node{
Data: h,
Next: nil,
}
if c.HeadNode == nil {
c.HeadNode = node
c.TailNode = node
return
}
node.Next = c.HeadNode
c.HeadNode = node
}
// 添加链表到尾部
func (c *chain) AddToTail(h Handler) {
node := &node{
Data: h,
Next: nil,
}
if c.HeadNode == nil || c.TailNode == nil {
c.HeadNode = node
c.TailNode = node
return
}
c.TailNode.Next = node
}
func (c *chain) Iterator(req *Req) error {
curr := c.HeadNode
for curr != nil { // 遍历链表
err := curr.Data.Handle(req); // 执行 Handle 方法
if err != nil {
return err
}
curr = curr.Next
}
return nil
}
|
这样链表的定义好了。使用者在使用的时候,需要实现 Handle 方法、添加到链表、调用 Iterator 即可。
下面创建一个结构体去实现 Handle 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| package create
import (
"errors"
"time"
"verify"
)
// 实例化一个验证器
var InfoVerifier = &infoVerifier{}
type infoVerfier struct {}
// 实现 Handle 方法
func (l *infoVerifier) Handle(req *Req) error {
if req.Info.CreateTime < time.Now().Unix() {
return errors.New("创建时间错误")
}
if req.Info.Uid < 1 {
return errors.New("创建人id错误")
}
if req.Info.ManagerId < 1 {
return errors.New("管理员id错误")
}
if !verify.IsBadMan(req.Info.Uid) {
return errors.New("请稍后再试")
}
return nil
}
|
在创建抽奖的时候,初始化链表、添加到链表、调用 Iterator
1
2
3
4
5
6
7
8
9
10
11
| package create
func CreateLottery(req *Req) {
c := chain.NewChain() // 初始化链表
c.AddToHead(InfoVerifier) // 添加验证器到链表
err := c.Iterator(req) // 遍历所有验证器
if err != nil {
response.JsonExitError(400, error)
}
...
}
|
这样的方法实现了非常好的扩展性。编写链表结构的人不需要考虑后面使用者的参数多么复杂,反正都统一放在 Param Structure 中,只要求使用者一定要实现 Handle 方法。然后在遍历方法 Iterator
中遍历链表并调用 Handle 就行。
使用者只需要创建一个结构体,实现 Handle 方法使结构体成为一个校验器,然后使用 AddToXXX
加入链表,调用 Iterator
方法即可。