我正在创建与elasticsearch的连接(但替换您喜欢的任何其他数据源),它将基于运行时的环境或配置文件参数。它看起来像这样:
(defn create-conn
"Connect to the given uri. This is a persistent conn managed by clj-http (apache)."
([uri]
( ;;; create a persistent connection using clj-http / elastic
...)
([]
(create-conn (or (System/getenv "ES_URL")
(cfg/get-url-from-config-file)
"http://localhost:9200"))))
因为连接在服务器的生命周期内不会改变,所以我只需要运行一次这个函数并缓存结果。有几种方法可以做到这一点:
1 - memoize
它 - 虽然这有效,但它并不像正确的方法,因为我只是缓存一件事
2 - 使用像Component或mount这样的状态管理器;因为我不是真正管理国家,只是设置和遗忘,而只是将它用于这一件事,感觉有点矫枉过正。例如,以下如何优于下面的#3?
; mount version -- good, but how is this better than #3 below?
(defstate conn :start (getconn))
(mount/start #'elastic/conn) ;; somewhere else, must start mount
3 - def
它。即使运行create-conn
实际上并不执行任何网络活动,我也不会在文件加载时运行它,如果我只是在其上执行常规def
会发生这种情况,所以我必须执行以下操作...请注意,getconn
功能是为了方便起见,因此我不必直接deref
conn
:
(def conn (delay (create-conn)))
(defn getconn [] @conn)
4 - 使用atom
- 直截了当,但这不是一个需要改变的var,并且它引入了不必要的状态:
(def conn (atom nil))
(def getconn []
(if-not @conn (reset! conn (create-conn)))
@conn)
5 - [insert your idea here]
在上面,我更喜欢3,因为它使用不可变数据,即使delay
有点笨拙。您的偏好是什么,无论是来自上面的列表,还是您自己的解决方案?
答案 0 :(得分:2)
答案 1 :(得分:0)
即使conn
在启动后没有发生变化,如果您重视集成测试,也可能希望针对不同的数据库运行相同的存储库功能。在REPL中加载项目并使用某个数据库开始提供请求后,您可能希望编写针对不同数据库运行的一些集成测试。如果您开发测试并并行处理请求,那么只是一个原子还不够好。最简单的,我认为最常用的方法是def
连接并将其作为参数传递给每个存储库函数(在测试中传递另一个值)。具有动态变量和原子的东西可以导致更少的代码,但结果更复杂。