如何优雅的写校验函数

有的时候,为了检查入参,会有很多项需要检查,如果一个一个if-else的去判断,会显得很low,先看一个比较丑的写法:

func checkQeuryParam(c *condition) bool {
    if c.offset > 100000 {
        return false
    }

    if c.limit > 100 {
        return false
    }

    if c.timebegin != "" {
        zone := time.FixedZone("CST", 8*3600)
        t, err := time.ParseInLocation("2006-01-02T15:04:05.000+0800", c.timebegin, zone)
        if err != nil {
            aialog.Error.Printf("Query time %s is invalid.\n", c.timebegin)
            return false
        }
        c.timebegin = t.Format("2006-01-02T15:04:05.000+0800")
    }

    //ip no check
    //come_from no check

    //event
    if c.event != "" {
        if c.event != "added" && c.event != "modified" && c.event != "deleted" {
            return false
        }
    }

    //ruleid
    if c.ruleid != "" {
        id, err := strconv.Atoi(c.ruleid)
        if err != nil {
            return false
        }

        //id range, temporary
        if id < 0 || id > 1000000 {
            return false
        }
    }

    if c.rulelevel != "" {
        level, err := strconv.Atoi(c.rulelevel)
        if err != nil {
            return false
        }

        //level range 0-16
        if level < 0 || level > 15 {
            return false
        }
    }

    if c.agentid != "" {
        if len(c.agentid) > 64 {
            return false
        }
    }

    //order
    if c.order != "" {
        if c.order != "desc" && c.order != "asc" {
            return false
        }
    }

    return true
}

1、易读性很差
2、修改麻烦
3、不易扩展
4、打印失败信息麻烦,需要每个异常分支添加

调整为如下方式:


func (c *condition)checkOffset() bool {
    if c.offset > 100000 {
        return false
    }

    return true
}

func (c *condition)checkLimit() bool {
    if c.limit > 100 {
        return false
    }

    return true
}

func (c *condition)checkTime() bool {
    if c.timebegin != "" {
        zone := time.FixedZone("CST", 8*3600)
        t, err := time.ParseInLocation("2006-01-02T15:04:05.000+0800", c.timebegin, zone)
        if err != nil {
            aialog.Error.Printf("Query time %s is invalid.\n", c.timebegin)
            return false
        }
        c.timebegin = t.Format("2006-01-02T15:04:05.000+0800")
    }

    return true
}

func (c *condition)checkEvent() bool {
    if c.event != "" {
        if c.event != "added" && c.event != "modified" && c.event != "deleted" {
            return false
        }
    }

    return true
}


func (c *condition)checkRuleid() bool {
    if c.ruleid != "" {
        id, err := strconv.Atoi(c.ruleid)
        if err != nil {
            return false
        }

        //id range, temporary
        if id < 0 || id > 1000000 {
            return false
        }
    }

    return true
}

func (c *condition)checkRulelevel() bool {
    if c.rulelevel != "" {
        level, err := strconv.Atoi(c.rulelevel)
        if err != nil {
            return false
        }

        //level range 0-16
        if level < 0 || level > 15 {
            return false
        }
    }

    return true
}

func (c *condition)checkAgentid() bool {
    if c.agentid != "" {
        if len(c.agentid) > 64 {
            return false
        }
    }

    return true
}

func (c *condition)checkOrder() bool {
    if c.order != "" {
        if c.order != "desc" && c.order != "asc" {
            return false
        }
    }

    return true
}

func checkQeuryParam(c *condition) bool {
    checks := []struct{
        name string
        fn   func() bool
    }{
        {"check offset", c.checkOffset},
        {"check limit", c.checkLimit},
        {"check time", c.checkTime},
        {"check event", c.checkEvent},
        {"check rule id", c.checkRuleid},
        {"check rule level", c.checkRulelevel},
        {"check agent id", c.checkAgentid},
        {"check order", c.checkOrder},
    }

    for _, check := range checks {
        if !check.fn() {
            aialog.Error.Printf("%s failed.\n", check.name)
            return false
        }
    }

    return true
}

1、条理清晰,易读性好
2、增加判断时,直接新增函数
3、打印失败信息也比较方便,且新增判断也不用新增打印

注:
对于函数名作为参数时,如果是传的方法,需要连对象一起传入。
如上的fn,传入时为c.checkXXX,c为这个方法的实际调用对象。


发表评论

电子邮件地址不会被公开。 必填项已用*标注