我从 Stuart 的演讲中了解到是这样的:
(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 个参数的函数,而 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 模块来创建一个 webapp,并将所需的依赖项作为参数传递。当然,您仍然需要有人使用正确的用户模块创建 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
将从您的 appmain
方法中调用。这只是意味着您需要切换到 lein-run 插件。
现在,鉴于您正在询问init
(来自我假设的 lein ring 插件),我猜您计划将您的 webapp 部署在容器中。由于 lein ring 插件必须在 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 容器,可能就足够了。
这只是为什么库比框架“更好”的另一个论点:)