Clojure中执行多规则校验并收集全部违规结果的最优方法是什么
Clojure 多谓词全量校验最优实现方案
现有方案的问题
你当前用cond的实现存在短路求值的问题:只要前面任意一个校验条件命中,后续所有校验逻辑都会直接跳过,自然无法收集到全部违规内容,不符合你的需求。
推荐实现思路
推荐采用规则集+全量遍历的函数式实现,完全避免副作用和异常,天然支持收集所有违规内容,且扩展性极强。
基础实现(无副作用,直接返回违规集合)
这是最符合Clojure函数式风格的写法,无需依赖add-violation这类可变操作:
;; 预定义所有校验规则,每个规则为 [校验谓词 违规信息] 结构,新增规则直接加项即可 (def validation-rules [[logic/check-one? "第一项校验不通过"] [logic/check-two? "第二项校验不通过"]]) (defn validate [arg] ;; 遍历所有规则,收集所有命中的违规信息,返回空集合则代表校验通过 (into [] (keep (fn [[pred violation-msg]] (when (pred arg) violation-msg))) validation-rules))
适配原有副作用操作的实现
如果你确实需要保留现有add-violation类的副作用逻辑,可以用doseq遍历执行:
(defn validate [arg] (doseq [[pred add-violation-fn] [[logic/check-one? add-violation-one] [logic/check-two? add-violation-two]]] (when (pred arg) (add-violation-fn))))
扩展:结构化违规信息
如果需要携带违规码、参数等结构化信息,可以把规则定义为map结构,适配性更高:
(def validation-rules [{:pred logic/check-one? :code :invalid-field-one :msg "字段1不符合业务规则"} {:pred logic/check-two? :code :invalid-field-two :msg "字段2不符合业务规则"}]) (defn validate [arg] (into [] (filter #((:pred %) arg)) validation-rules))
方案优势
- 完全符合需求:不抛出异常,全量执行所有校验规则,可收集所有违规内容
- 纯函数实现无副作用,易测试易调试,调用后直接通过返回集合是否为空判断校验是否通过
- 易扩展,新增校验规则无需修改
validate核心逻辑,只需要在规则集里加对应项即可
内容的提问来源于stack exchange,提问作者brenobarreto
相关产品推荐
相关产品推荐

