3

外部の「サービスプロバイダー」が実装できるClojureのインターフェースを定義する慣用的な方法を探しています。私のアプリケーションは、実行時にサービスプロバイダーモジュールを見つけてインスタンス化し、特定の責任をそれに委任します。

たとえば、RPCメカニズムを実装していて、構成時にカスタムミドルウェアを注入できるようにしたいとします。このミドルウェアは、メッセージの前処理、メッセージの破棄、メッセージハンドラーのロギングによるラップなどを行うことができます。

Javaリフレクションにフォールバックする場合、これを行う方法はいくつか知っていますが、Clojureに実装すると理解に役立つと思います。

(ここでは、一般的な意味でSPIを使用していることに注意してください。特に、JARファイル仕様で定義されている方法については言及していません) 。

ありがとう

4

2 に答える 2

7

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
于 2009-11-28T01:58:58.910 に答える
6

Clojure は非常に動的な言語です。コンパイル時に実行できることは、ほとんどすべて実行時に実行できます。「デプロイメント構成」は、実行時にアプリケーションにロードされる単純な clojure ソース ファイルである可能性があります。呼び出すだけです(「my-config.clj」をロードしてください)。本当に必要な場合は、特定の動的スコープ内の関数をオーバーライドすることもできることに注意してください。そのため、任意の関数 (コア関数を含む) を別の関数でラップして、引数、戻り値、および実行にかかった時間をログに記録することができます。これを行う方法の例については、clojure.contrib.trace を見てください。

于 2009-11-30T08:23:09.740 に答える