Clojure 如何在Compojure应用程序中除去全局数据
上面有一张便条,上面写着:Clojure 如何在Compojure应用程序中除去全局数据,clojure,compojure,Clojure,Compojure,上面有一张便条,上面写着: 在顶层定义参考和原子基本上是通过单态的全局可变态,请避免 建议使用构造函数返回要使用的状态变量,然后将该状态传递给每个函数 我认为这是一个很好的建议,但我不完全确定如何在环/复合应用程序中实现这一点。有谁能给出一个具体的例子来说明这是如何工作的 我特别感兴趣的是如何以这种方式将defroutes、init和app组合在一起,并去除该范围内的全局变量。在许多情况下,您需要全局状态,因此无法避免它,你能做的就是正确地管理它,我想这就是两点所说的: 这不是一个好办法:
- 在顶层定义参考和原子基本上是通过单态的全局可变态,请避免
- 建议使用构造函数返回要使用的状态变量,然后将该状态传递给每个函数
我特别感兴趣的是如何以这种方式将
defroutes
、init
和app
组合在一起,并去除该范围内的全局变量。在许多情况下,您需要全局状态,因此无法避免它,你能做的就是正确地管理它,我想这就是两点所说的:
这不是一个好办法:
(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.
基本上,对任何类型状态的所有访问都应该从应用程序的其他部分抽象出来。您的所有业务逻辑功能都应该将状态作为参数并返回新状态,而不是自己选择状态并直接更新它。我从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
将从应用程序main
方法调用。这只意味着您需要切换到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容器,那么这可能已经足够好了
这只是图书馆为什么比框架“更好”的另一个论点:)@hsestupin:这是一个问题还是肯定:)肯定ofc。我的意思是,当状态不能通过直接引用进行更改时,我完全同意这种方法。这看起来像是封装的全局状态,但仍然很糟糕。请看讲座。@dAni你能谈谈讲座中的建议吗,特别是解决方案(而不仅仅是说它不好)@Ankur抱歉,我以为St3fan没有看过演示文稿,只看过摘要,所以Stuart的完整演示足以解释它的意思(30分钟!)。有关如何将其应用于compojure的完整说明,请参见我的回复。