2013-06-09 8 views
6

Sto cercando di implementare le idee dallo http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded nella mia base di codici.Evitare lo stato globale nel livello DAO di un'applicazione Clojure

Ho uno strato dao, dove ora devo passare in un database per evitare lo stato globale. Una cosa che mi sta buttando fuori è la frase:

Qualsiasi funzione che necessiti di uno di questi componenti deve considerarla come un parametro . Questo non è così oneroso come potrebbe sembrare: ogni funzione ottiene, al massimo, un argomento in più che fornisce il "contesto" in cui opera . Quel contesto potrebbe essere l'intero oggetto di sistema, ma più spesso sarà un sottoinsieme. Con l'uso giudizioso delle chiusure lessicali, gli argomenti extra scompaiono dalla maggior parte del codice.

Dove dovrei utilizzare le chiusure per evitare di passare lo stato globale per ogni chiamata? Un esempio potrebbe essere quello di creare una funzione init nello strato DAO, qualcosa di simile:

(defprotocol Persistable 
    (collection-name [this])) 

(def save nil) 

(defn init [{:keys [db]}] 
    (alter-var-root #'save (fn [_] (fn [obj] (mc/insert-and-return db (collection-name obj) obj WriteConcern/SAFE))))) 

In questo modo posso avviare il mio strato DAO dal sistema/avvio funzione come questa:

(defn start 
    [{:keys [db] :as system}] 
    (let [d (-> db 
       (mc/connect) 
       (mc/get-db "my-test"))] 
    (dao/init d) 
    (assoc system :db d))) 

Questo funziona, ma sembra un po 'icky. C'è un modo migliore? Se possibile, vorrei evitare di forzare i client del mio livello dao a dover passare un database ogni volta che usa una funzione.

risposta

8

È possibile utilizzare la funzione di ordine superiore per rappresentare il livello DAO - questo è il punto cruciale della programmazione funzionale, utilizzando le funzioni per rappresentare le parti piccole e grandi del sistema. Quindi hai una funzione di ordine superiore che accetta la connessione DB come param e ti restituisce un'altra funzione che puoi usare per chiamare varie operazioni come salvare, cancellare ecc. Sul database. Di seguito è riportato un esempio:

(defn db-layer [db-connection] 
    (let [db-operations {:save (fn [obj] (save db-connection obj)) 
         :delete (fn [obj] (delete db-connection obj)) 
         :query (fn [query] (query db-connection query))}] 
    (fn [operation & params] 
     (-> (db-operations operation) (apply params))))) 

L'utilizzo di livello DB:

(let [my-db (create-database) 
     db-layer-fn (db-layer my-db)] 
    (db-layer-fn :save "abc") 
    (db-layer-fn :delete "abc")) 

Questo è solo un esempio di come funzioni di ordine superiore possono permettere di creare una sorta di contesto per un'altra serie di funzioni. Puoi sfruttare ulteriormente questo concetto combinandolo con altre funzionalità di Clojure come i protocolli.

Problemi correlati