我有许多命名空间,每个命名空间都包含一个具有相同名称的函数,例如:
(ns myns.resourceX
...)
(defn create
(println "resourceX/create"))
(ns test)
(myns.resourceX/create)
(myns.resourceY/create)
Run Code Online (Sandbox Code Playgroud)
(您可以想象有resourceX、resourceY、resourceZ等。实际create函数最终会发送 HTTP POST 并返回响应,但这在这里并不重要。)
现在,在另一个命名空间中,我想定义一个带有两个参数的函数:资源名称数组(即命名空间名称)和函数名称,例如:
(defn do-verb
[verb res-type]
(??))
Run Code Online (Sandbox Code Playgroud)
所以我可以写:
(do-verb :create :resourceX)
Run Code Online (Sandbox Code Playgroud)
与以下效果相同:
(myns.resourceX/create)
Run Code Online (Sandbox Code Playgroud)
我尝试过的一件事是使用ns-resolve,例如:
(defn do-verb
[verb res-type & params]
(apply (ns-resolve
(symbol (clojure.string/join ["myns." (name res-type)]))
(symbol (name verb)))
params))
但我不确定是否使用ns-resolve-- 似乎是一种黑客行为。
我探索的另一种可能性是定义一个映射来将符号与函数相关联:
(def convert-fns
{:resourceX {:create resourceX/create}
:resourceY {:create resourceY/create}
...})
(defn do-verb [verb res-type & params]
(apply (get-in convert-fns [res-type verb]) params))
Run Code Online (Sandbox Code Playgroud)
但这对我来说有一个缺点,即convert-fns每次添加新资源时都需要进行修改。
有没有其他方法可以替代这些方法?
如果你确实想在命名空间中进行动态查找,你可以使用ns-publics函数。
请参阅以下片段:
(defn do-verb [verb res-type & params]
(let [ns-symbol (symbol (str "myns." (name res-type)))
publics (ns-publics ns-symbol)
;; Looking up function we need
func (publics (symbol (name verb)))]
(apply func params)))
Run Code Online (Sandbox Code Playgroud)
然而,这看起来仍然有点老套,与使用ns-resolve.
因此,我建议您使您的do-verb函数成为多方法。
(defmulti do-verb (fn [verb res-type & args] [verb res-type]))
Run Code Online (Sandbox Code Playgroud)
之后,do-verb针对您的每种verb类型和资源类型实施:
(defmethod do-verb [:create :resourceX] [_ _ & args] ...)
Run Code Online (Sandbox Code Playgroud)
并按如下方式调用它:
(do-verb :create :resourceX ...)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,签名完美匹配。