2015-02-11 2 views
2

Я Расщепление части моего приложения в библиотекуинъекция зависимости между приложением и библиотекой в ​​Clojure

Функциональность библиотеки имеет определенную зависимость, которые должны быть введены в приложении. Я смоделировал это с протоколом

(defprotocol MyLibDependencies 
    (some-injected-capability [x])) 

(defrecord Foo [y]) 
(defrecord Bar [y]) 

(defmulti render-response class) 

(defmethod render-response Foo [val] {:ok (some-injected-capability (:y val))}) 
(defmethod render-response Bar [val] {:err (some-injected-capability (:y val))}) 

и здесь, в приложении я могу обеспечить реализацию:

(extend-type Object 
    MyLibDependencies 
    (some-injected-capability [x] (inc x))) 

(comment 
    (render-response (Foo. 10)) ;; => {:ok 11} 
    (render-response (Bar. 10)) ;; => {:err 11} 
) 

Это работает, однако он чувствует, как злоупотребление протоколов, потому что не нужна ни полиморфной отправка , а также не требует ввода аргумента (для протокола требуется хотя бы один аргумент для отправки против его класса). Какие у меня варианты?

Обратите внимание, что записи Foo и Bar являются типами доменов библиотеки, а метод render-response также является доменом библиотеки. Мне не обязательно, как я их определяю, но абстракции, которые они представляют, являются библиотечной областью.

+2

Вместо того, чтобы вставлять все вызовы на сервере, необходимо вызвать методы протокола и предоставить клиенту тип, реализующий протокол. – noisesmith

+0

Я не понимаю, как это отличается от того, что у меня есть, можете ли вы отправить код? –

ответ

1

Это немного ближе к тому, как вы обычно видите протоколы, используемые для обеспечения функциональности от клиентского кода в библиотеке:

;; lib.clj 
(defprotocol MyLibDependencies 
    (provide-status [this x]) 
    (provide-response [this x])) 

(defn render-response 
    [responder val] 
    {:status (provide-status responder val) 
    :code (provide-response responder val)}) 

;; client.clj 
(defrecord Foo [y] 
    MyLibDependencies 
    (provide-status [this val] 
    (if (even? val) 
     :ok 
     :err)) 
    (provide-response [this val] 
    (+ y val))) 

(defrecord Bar [y] 
    MyLibDependencies 
    (provide-status [this val] 
    (if (odd? val) 
     :ok 
     :err)) 
    (provide-response [this val] 
    (+ y val))) 


(comment 
    (render-response (Bar. 10) 1) ;; => {:status :ok :code 11} 
    (render-response (Foo. 10) 1) ;; => {:status :err :code 11} 
) 

Есть многочисленные примеры этого стиля Clojure кода в дикой природе - на самом деле большинство основных функций, которые составляют сам Clojure, в конечном итоге разрешают вызовы методов протокола, предоставляемых конкретной используемой структурой данных, или многотодовые вызовы, распространяемые для отдельных типов данных.

+0

Можно ли это сделать, сохранив записи и метод, определенные в библиотеке? –

+0

Вы можете использовать расширенный тип, чтобы добавить реализацию протокола в запись от клиента, но я не вижу большой пользы для определения записи в lib. Модель адаптации к использованию типа, переданного от клиента, такая же, как и конструкция, предназначенная для использования аргументов, переданных явно, а не глобального состояния. Помните, что вы можете связать любой ключ, который вам нравится, на любую запись независимо от типа, включая ключ с именами, характерный для вашей библиотеки. – noisesmith