我将应用程序的一部分拆分为库
库功能具有必须由应用程序注入的某些依赖项。我用协议
建模了这个(defprotocol MyLibDependencies
(some-injected-capability [x]))
(defrecord Foo [y])
(defrecord Bar [y])
(defmulti render-response class)
(defmethod render-response Foo [val] {:ok (some-injected-capability (:y val))})
(defmethod render-response Bar [val] {:err (some-injected-capability (:y val))})
在应用程序中我可以提供一个实现:
(extend-type Object
MyLibDependencies
(some-injected-capability [x] (inc x)))
(comment
(render-response (Foo. 10)) ;; => {:ok 11}
(render-response (Bar. 10)) ;; => {:err 11}
)
这有效,但感觉就像是滥用协议,因为我既不需要多态分派,也不需要注入函数需要参数(协议需要至少一个参数来调度它的类)。我有什么选择?
请注意,记录Foo和Bar是库域类型,render-response
方法也是库域。我不一定关心我如何定义它们,但它们代表的抽象是库域。
答案 0 :(得分:1)
这更接近于您通常会看到用于从客户端代码向库提供功能的协议:
;; lib.clj
(defprotocol MyLibDependencies
(provide-status [this x])
(provide-response [this x]))
(defn render-response
[responder val]
{:status (provide-status responder val)
:code (provide-response responder val)})
;; client.clj
(defrecord Foo [y]
MyLibDependencies
(provide-status [this val]
(if (even? val)
:ok
:err))
(provide-response [this val]
(+ y val)))
(defrecord Bar [y]
MyLibDependencies
(provide-status [this val]
(if (odd? val)
:ok
:err))
(provide-response [this val]
(+ y val)))
(comment
(render-response (Bar. 10) 1) ;; => {:status :ok :code 11}
(render-response (Foo. 10) 1) ;; => {:status :err :code 11}
)
这种Clojure代码风格有很多例子 - 实际上构成Clojure本身的大多数核心函数最终都解析为调用所使用的特定数据结构提供的协议方法,或者扩展了多方法调用对于各个数据类型。