Определение SPI в Clojure - PullRequest
       15

Определение SPI в Clojure

3 голосов
/ 27 ноября 2009

Я ищу идиоматический способ (ы) для определения интерфейса в Clojure, который может быть реализован внешним «поставщиком услуг». Мое приложение будет находить и создавать экземпляр модуля поставщика услуг во время выполнения и делегировать ему определенные обязанности.

Допустим, например, что я внедряю механизм RPC и хочу разрешить внедрение специального промежуточного программного обеспечения во время конфигурации. Это промежуточное программное обеспечение может предварительно обрабатывать сообщения, удалять сообщения, оборачивать обработчик сообщений журналом и т. Д.

Я знаю несколько способов сделать это, если вернусь к рефлексии Java, но чувствую, что реализация этого в Clojure поможет моему пониманию.

(Обратите внимание, я использую SPI в общем смысле, не ссылаясь на то, как он определен в спецификации файла JAR )

Спасибо

Ответы [ 2 ]

6 голосов
/ 30 ноября 2009

Clojure - очень динамичный язык: почти все, что можно сделать во время компиляции, можно сделать во время выполнения. Ваша «конфигурация развертывания» может быть просто исходным файлом clojure, который загружается в приложение во время выполнения. Просто позвоните (загрузите "my-config.clj"). Обратите внимание, что вы можете даже переопределить функции в определенной динамической области, если вы действительно этого хотите, так что вы можете обернуть любую функцию (включая основные функции) другой, в которой записываются их аргументы, возвращаемое значение и продолжительность взял бежать. Посмотрите на clojure.contrib.trace для примера того, как это сделать.

6 голосов
/ 28 ноября 2009

Compojure использует "middleware" для обработки 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...