我正在寻找一种惯用的方法来定义Clojure中可以由外部“服务提供商”实现的接口。我的应用程序将在运行时定位并实例化服务提供者模块,并将某些职责委托给它。

例如,假设我正在实现RPC机制,并且我希望允许在配置时注入自定义中间件。此中间件可以预处理消息,丢弃消息,使用日志包装消息处理程序等

如果我回到Java反射,我知道有几种方法可以做到这一点,但我觉得在Clojure中实现它会有助于我的理解。

(注意,我在这里使用的是一般意义上的SPI,而不是特别指的是它在 JAR文件规范

由于

有帮助吗?

解决方案

Clojure是一种非常动态的语言:几乎任何可以在编译时完成的事情都可以在运行时完成。您的“部署配置”可能只是一个在运行时加载到应用程序中的clojure源文件。只需调用(load" my-config.clj")。请注意,如果您真的想要,您甚至可以覆盖特定动态范围内的函数,因此您可以将任何函数(包括核心函数)与另一个函数包含在一起,即记录其参数,返回值和多长时间他们跑了。有关如何执行此操作的示例,请查看clojure.contrib.trace。

其他提示

Compojure 使用“middleware”,您可以查看其实现。 “处理程序”在Compojure中是一个接收请求并返回响应的函数。 (请求和响应都是Clojure哈希映射。)“中间件”。是一个接受处理函数的函数,并返回一个不同的处理函数。中间件可以改变请求,响应或两者;它可以调用它传递的处理程序(如果需要,可以反复执行)或短路并忽略处理程序等。您可以通过这种方式将处理程序包装在其他处理程序中。

由于函数是第一类对象,因此它非常轻量级且易于实现和使用。但是,它不会像在Java接口中那样在编译时强制执行任何操作。这都是遵循惯例和鸭子打字的问题。 协议最终可能对此任务有利,但它们不会继续有一段时间可用(可能在Clojure 2.0中?)

不确定这是否是您想要的,但这是一个非常基本的版本:

;; Handler
(defn default [msg]
  {:from "Server"
   :to (:from msg)
   :response "Hi there."})

;; Middleware
(defn logger [handler]
  (fn [msg]
    (println "LOGGING MESSAGE:" (pr-str msg))
    (handler msg)))

(defn datestamper [handler]
  (fn [msg]
    (assoc (handler msg)
      :datestamp (.getTime (java.util.Calendar/getInstance)))))

(defn short-circuit [handler]
  (fn [msg]
    {:from "Ninja"
     :to (:from msg)
     :response "I intercepted your message."}))

;; This would do something with a response (send it to a remote server etc.)
(defn do-something [response]
  (println ">>>> Response:" (pr-str response)))

;; Given a message and maybe a handler, handle the message
(defn process-message
  ([msg] (process-message msg identity))
  ([msg handler]
     (do-something ((-> default handler) msg))))

然后:

user> (def msg {:from "Chester" :to "Server" :message "Hello?"})
#'user/msg
user> (process-message msg)
>>>> Response: {:from "Server", :to "Chester", :response "Hi there."}
nil
user> (process-message msg logger)
LOGGING MESSAGE: {:from "Chester", :to "Server", :message "Hello?"}
>>>> Response: {:from "Server", :to "Chester", :response "Hi there."}
nil
user> (process-message msg (comp logger datestamper))
LOGGING MESSAGE: {:from "Chester", :to "Server", :message "Hello?"}
>>>> Response: {:datestamp #<Date Fri Nov 27 17:50:29 PST 2009>, :from "Server", :to "Chester", :response "Hi there."}
nil
user> (process-message msg (comp short-circuit logger datestamper))
>>>> Response: {:from "Ninja", :to "Chester", :response "I intercepted your message."}
nil
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top