Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 如何在Compojure应用程序中除去全局数据_Clojure_Compojure - Fatal编程技术网

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的完整说明,请参见我的回复。