2013-03-24 42 views
5

http://mindbat.com/2013/03/clojurewest-2013-day-one-notes/有一張紙條,上面寫着:如何在應用程序的Compojure擺脫全球數據

  • 在頂層def'ing裁判和原子基本上是通過全球的單身可變狀態,請避免
  • 推薦使用構造函數來您想使用的狀態變量,然後傳遞狀態一起到各功能

我覺得這是很好的建議,但我不完全知道如何在Ring/Compojure應用程序中執行此操作。任何人都可以給出一個具體的例子,說明這將如何工

我特別感興趣的是如何結合defroutes,initapp這樣一起去掉那個範圍內的全局變量。

回答

2

有你需要的全局狀態,因此你無法避免這一點很多情況下,你可以做什麼管理得當,我想這就是2點講:

不是一個好辦法:

(ns data) 
(def users (atom [])) 

(ns pages) 
(defn home [] 
    (do-something data/@users) 

(defn save [] 
    (let [u @users] 
     (swap! data/users ....) 

好方法:

(ns data) 
(def- users (atom [])) 
(defn get-users [] @users) 
(defn update-user [user] (swap! @users ...)) 

(ns pages) 
; use the functions exposed by data ns to interact with data rather then poking the atom directly. 

基本上所有的訪問任何類型的國家應與其他被抽象掉部分應用程序。你所有的業務邏輯函數都應該將狀態作爲參數並返回新的狀態,而不是直接選擇狀態並更新它。

+0

y!這是一個好方法。 – hsestupin 2013-03-25 05:55:46

+0

@hsestupin:這是一個問題或肯定:) – Ankur 2013-03-25 06:06:29

+0

肯定c。我的意思是,當國家不能通過直接引用而改變時,我完全同意這種做法。 – hsestupin 2013-03-25 08:52:40

5

我從斯圖爾特的談話瞭解什麼是這樣的:

(ns state.core) 

(defn create-user-module [] (atom [])) 

(defn add-user [module user] 
    (swap! module conj user)) 

(defn get-users [module] 
    @module) 

現在有你的「芯」作爲操作狀態的功能沒有全局狀態指望得到它作爲一個參數。這些可以輕鬆進行測試,因爲您可以爲每個測試創建一個「用戶模塊」的新實例。另外,這個模塊的客戶端不應該關心他們在create-user-module函數中獲得什麼,他們應該只是傳遞它而不檢查它,這樣你可以隨時更改用戶模塊的實現。如果您要有多個實現,Stuart還會談談如何爲這些模塊創建協議。

試圖回答你的問題,一個環適配器僅1個PARAM的功能,並且的Compojure只是一個路由庫,因此你可以使用閉包,如創建一個web應用程序:

(ns state.web 
    (:use compojure.core) 
    (:require [state.core :as core])) 

(defn web-module [user-module] 
    (routes 
    (GET "/all" [] (core/get-users user-module)))) 

現在你可以調用Web模塊來創建Web應用程序,並將其作爲參數傳遞給需要的依賴項。當然,你仍然需要有人來創建正確的用戶模塊的Web應用程序,所以你只需要一個電線都在一起了「主要」功能:

(ns state.main 
    (:require state.core 
      state.web) 
    (:use ring.adapter.jetty)) 

(defn start [] 
    (let [user-module (state.core/create-user-module) 
     web-module (state.web/web-module user-module)] 
    (run-jetty web-module {:port 3000 :join? false}))) 

(defn stop [app] 
    (.stop app)) 

start將您的應用程序main方法被調用。這只是意味着你需要切換到lein-run插件。

現在,鑑於您詢問的是init(我假設來自lein ring插件),我想您打算將webapp部署到容器中。由於雷音環插件擁有了Java Servlet FW限制範圍內,並且該處理結束編譯爲Java Servlet的工作,或許您可以做的最好的是一樣的東西:

(ns state.web 
    (:use compojure.core) 
    (:require [state.core :as core])) 

(def module-deps (atom {}) 

(defn init-app [] (swap! module-deps conj [:user-module (core/create-user-module)])) 

(defroutes web-module [] 
    (GET "/all" [] (core/get-users (:user-module @module-deps)))) 

這仍然意味着你的核心命名空間很容易測試,但是您仍然在Web命名空間中擁有全局狀態,但是我認爲這是「正確」封裝的,如果您必須使用java容器,則可能已經足夠好了。

而這只是爲什麼圖書館比框架「更好」的另一個理由:)