clojure中应用程序和库之间的依赖项注入

clojure中应用程序和库之间的依赖项注入,clojure,dependency-injection,Clojure,Dependency Injection,我正在将我的应用程序的一部分拆分为一个库 库功能具有某些依赖项,应用程序必须注入这些依赖项。我已经用协议对此进行了建模 (defprotocol MyLibDependencies (some-injected-capability [x])) (defrecord Foo [y]) (defrecord Bar [y]) (defmulti render-response class) (defmethod render-response Foo [val] {:ok (some-i

我正在将我的应用程序的一部分拆分为一个库

库功能具有某些依赖项,应用程序必须注入这些依赖项。我已经用协议对此进行了建模

(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
方法也是库域。我不一定关心如何定义它们,但它们所表示的抽象是库域。

这与您通常看到的用于提供从客户端代码到库的功能的协议有点接近:

;; 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本身的大多数核心函数最终都会解析为对所使用的特定数据结构提供的协议方法的调用,或者对单个数据类型进行扩展的多方法调用。

而不是注入,让服务器中的所有调用都是对协议方法的调用,并让客户端提供实现协议的类型。我不明白这与我所拥有的有什么不同,您可以发布代码吗?可以在保留库中定义的记录和方法的同时进行吗?您可以使用extend type将协议实现从客户端添加到记录中,但我并不认为在库中定义记录有什么好处。适应使用从客户机传入的类型的模式与为了使用显式传入的参数而不是全局状态而进行的设计相同。请记住,您可以将任何喜欢的键关联到任何记录上,而不管其类型如何,包括特定于您的库的命名空间键。