Pergunta

Eu estou procurando uma maneira idiomática (s) para definir uma interface em Clojure que pode ser implementado por um "prestador de serviços" externo. Meu aplicativo seria localizar e instanciar o módulo fornecedor de serviços em tempo de execução e delegar determinadas responsabilidades a ele.

Vamos dizer, por exemplo, que eu estou implementando um mecanismo RPC e eu quero permitir que um middleware personalizado para ser injetado no momento da configuração. Este middleware pode pré-processar a mensagem, as mensagens de descarte, enrole o manipulador de mensagem com o registro, etc.

Eu sei que várias maneiras de fazer isso se eu cair de volta para a reflexão Java, mas sentem que a sua aplicação em Clojure ajudaria o meu entendimento.

(Note, eu estou usando SPI em um sentido geral aqui, não se referindo especificamente à forma como é definido no JAR especificação de arquivo )

Graças

Foi útil?

Solução

Clojure é uma linguagem muito dinâmico: quase tudo o que pode ser feito em tempo de compilação pode ser feito em tempo de execução. Seu "configuração de implantação" poderia ser simplesmente um arquivo de origem clojure que é carregado para o aplicativo em tempo de execução. chamada Just (load "minha-config.clj"). Note que você pode até mesmo substituir funções em um escopo dinâmica particular, se você realmente quer, para que possa envolvê qualquer função (incluindo funções essenciais) com um outro que digamos registra seus argumentos, valor de retorno e quanto tempo eles levaram para ser executado. Ter um olhar para clojure.contrib.trace para um exemplo de como fazer isso.

Outras dicas

Compojure usos "middleware" para lidar com solicitações HTTP, você pode olhar para a sua implementação. A "manipulador" em Compojure é uma função que recebe um pedido e retorna uma resposta. (Solicitação e resposta são ambos de hash-mapas Clojure.) "Middleware" é uma função que leva uma função de manipulador, e retorna uma função de manipulador diferente. Middleware pode alterar o pedido, a resposta, ou ambos; ele pode chamar o manipulador é passado (repetidamente, se quiser) ou curto-circuito e ignoram o manipulador, etc. Você pode embrulhar manipuladores em outros manipuladores desta forma, em qualquer combinação.

Graças a funções que são objetos de primeira classe, isso é muito leve e fácil de implementar e usar. No entanto, não impor qualquer coisa em tempo de compilação como você deseja obter a partir de uma interface Java; é tudo uma questão de seguir convenções e pato-digitação. Protocolos pode ser bom para esta tarefa, eventualmente, mas eles não vão estar disponível por um tempo (provavelmente em Clojure 2.0?)

Não tenho certeza se é isso que você quer, mas aqui é uma versão muito rudimentar:

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

Depois:

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top