我有一个包含一组路由和处理程序的compojure应用程序。
(defroutes app-routes
(GET "/stuff/:id" [:as request] (stuff/get-stuff request))
(POST "/stuff/" [:as request] (stuff/create-stuff request))
每个处理程序验证其输入,如此
(defn create-stuff
[request]
(my-validation/validate-request
request
my-validation/create-stuff-validator
stuff-ok-fn))
验证码基于Metis,如下所示:
(metis/defvalidator :create-stuff-validator
[:db :presence])
(defn validate-request
[request request-validator ok-function]
(let [validation-result (request-validator request)]
(if (empty? validation-result)
(ok-function request)
(bad-request validation-result))))
我的问题是create-stuff中的代码在每个路由处理程序中都是重复的; get-stuff函数看起来像create-stuff处理程序。唯一不同的是它们的验证器功能和它们的验证功能。
如何以惯用的Clojure方式抽象出这种重复?
答案 0 :(得分:1)
由于这是一种函数式语言,我建议将处理程序区分的函数传递给通用处理函数。
;;; in core.clj
(defroutes app-routes
(GET "/stuff/:id" [:as request]
(handlers/handle
my-validation/get-stuff-validator
stuff/get-stuff-ok-fn
request))
(POST "/stuff/" [:as request]
(handlers/handle
my-validation/create-stuff-validator
stuff/create-stuff-ok-fn
request)))
;;; in handlers.clj
(defn handle
[validator action request]
(let [validation-result (validator request)]
(if (empty? validation-result)
(action request)
(bad-request validation-result))))
从风格上讲,如果你避免使用the smurf naming convention,我建议代码更易于阅读。命名空间告诉我们您是否正在验证,或者“东西”是您正在操作的东西,您不需要将它包含在函数的名称中。另外,你传递一个应该是可调用的参数的事实就足够了,你不需要在函数的名称中加入fn,因为ok分支告诉我们这是事情要做的事实当事情顺利的时候。
;;; in core.clj
(defroutes app-routes
(GET "/stuff/:id" [:as request]
(handlers/handle
my-validation/get-stuff
stuff/get
request))
(POST "/stuff/" [:as request]
(handlers/handle
my-validation/create-stuff
stuff/create
request)))
;;; in handlers.clj
(defn handle
[validator ok request]
(let [errors (validator request)]
(if (empty? errors)
(ok request)
(bad-request errors))))
如果你可以在不失去清晰度的情况下减少冗长,就可以提高正确性,因为错误会隐藏在冗长中。