質問
外部の「サービスプロバイダー」が実装できるClojureのインターフェイスを定義するための慣用的な方法を探しています。私のアプリケーションは、実行時にサービスプロバイダーモジュールを見つけてインスタンス化し、特定の責任を委任します。
たとえば、RPCメカニズムを実装しており、構成時にカスタムミドルウェアを挿入できるようにしたいとします。このミドルウェアは、メッセージの前処理、メッセージの破棄、ログでのメッセージハンドラのラップなどを行うことができます。
Javaのリフレクションに戻る場合、これを行う方法はいくつかありますが、Clojureで実装すると理解に役立つと感じています。
(注、ここではSPIを一般的な意味で使用していますが、 JARファイルの仕様)
ありがとう
解決
Clojureは非常に動的な言語です。コンパイル時に実行できるほとんどすべてのことが実行時に実行できます。 「展開構成」単に実行時にアプリケーションにロードされるclojureソースファイルである可能性があります。呼び出す(load" my-config.clj")。本当に必要な場合は、特定の動的スコープ内の関数をオーバーライドすることもできます。そのため、任意の関数(コア関数を含む)を、引数、戻り値、および時間を記録する別の関数でラップできます彼らは走りました。これを行う方法の例については、clojure.contrib.traceをご覧ください。
他のヒント
Compojure は"ミドルウェア" を使用してHTTPリクエストを処理する場合、その実装を確認できます。 「ハンドラ」 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