Question

Je recherche un (des) moyen (s) idiomatique (s) pour définir une interface dans Clojure pouvant être implémentée par un "fournisseur de service" externe. Mon application localiserait et instancierait le module de fournisseur de services au moment de l'exécution et lui déléguerait certaines responsabilités.

Supposons, par exemple, que je mette en place un mécanisme RPC et que je souhaite autoriser l’injection d’un middleware personnalisé au moment de la configuration. Ce middleware peut prétraiter le message, le supprimer, envelopper le gestionnaire de messages avec la journalisation, etc.

Je connais plusieurs façons de procéder si je retombe sur la réflexion Java, mais j’estime que son implémentation dans Clojure aiderait ma compréhension.

(Remarque, j'utilise SPI dans un sens général, ne faisant pas spécifiquement référence à la façon dont il est défini dans Spécification du fichier JAR )

Merci

Était-ce utile?

La solution

Clojure est un langage très dynamique: presque tout ce qui peut être fait au moment de la compilation peut être fait au moment de l’exécution. Votre " configuration de déploiement " pourrait simplement être un fichier source clojure qui sera chargé dans l'application au moment de l'exécution. Il suffit d'appeler (chargez "my-config.clj"). Notez que vous pouvez même remplacer des fonctions dans une étendue dynamique particulière si vous le souhaitez vraiment. Ainsi, vous pouvez envelopper toute fonction (y compris les fonctions principales) avec une autre fonction qui dit enregistre leurs arguments, leur valeur de retour et leur durée. ils ont pris à courir. Jetez un œil à clojure.contrib.trace pour voir un exemple de la procédure à suivre.

Autres conseils

Compojure utilise "middleware" pour gérer les requêtes HTTP, vous pouvez regarder son implémentation. Un " gestionnaire " dans Compojure est une fonction qui prend une requête et renvoie une réponse. (La demande et la réponse sont toutes les deux des cartes de hachage Clojure.) & Quot; Middleware " est une fonction qui prend une fonction de gestionnaire et renvoie une fonction de gestionnaire différente. Le middleware peut modifier la demande, la réponse ou les deux. il peut appeler le gestionnaire qui est passé (à plusieurs reprises s'il le souhaite) ou court-circuiter et ignorer le gestionnaire, etc. Vous pouvez encapsuler ainsi les gestionnaires dans d'autres gestionnaires de cette manière.

Les fonctions étant des objets de première classe, elles sont très légères et faciles à mettre en œuvre et à utiliser. Cependant, il n’impose aucune contrainte au moment de la compilation, contrairement à une interface Java; tout est une question de convention et de typage de canard. Les protocoles pourraient éventuellement être utiles pour cette tâche, mais ils ne se poursuivront pas. être disponible pendant un moment (probablement dans Clojure 2.0?)

Je ne sais pas si c'est ce que vous voulez, mais voici une version très rudimentaire:

;; 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))))

Ensuite:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top