不是您的问题的直接答案,但您使用的一般模式更常见:拥有一个保存连接参数(到数据库或另一台服务器)的有状态数据结构。大多数框架都扭转了这一点:不像你那样从保存连接参数的函数中调用你的函数,它们具有接受连接数据结构作为参数的函数。
例如,给定一个数据库连接conn,一个典型的虚构数据库库可能如下所示(注意:为清楚起见,示例已简化):
(let [conn (make-db-connection :host .... :user ....)]
(read-from-db conn :user))
在为消息传递框架(例如 RabbitMQ)使用库时,可能如下所示:
(let [conn (make-amqp-connection :host .... :port ...)]
(send-message conn :my-queue "hello world"))
在这两种情况下,都有一个conn数据结构用于对库函数的所有后续调用。在 OO 语言中,您将拥有一个全局的、有状态的对象来保存连接(在 Java 领域可能是一个单例)。在 Clojure 中,库通常使用with-...宏来处理此问题,该宏将特定连接绑定到内部使用的动态 var:
(with-db-connection (make-db-connection ....)
(read-from-db :user ....))
(with-rabbit-connection (make-rabbitmq-connection ....)
(send-message :my-queue "hello world"))
这是实现此模式的(虚构)示例。假设一个连接是一个 Java 对象:
;; a var to hold the connection
(def ^:dynamic *current-connection* nil)
(defmacro with-connection [conn & body]
`(binding [*current-connection* ~conn]
~@body))
;; send-msg is using the connection object bound to
;; the *current-connetion* var
(defn send-msg [msg]
(.sendMessage *current-connection* msg))
;; usage:
(with-connection conn
(send-msg "hello world!"))
如果您想花哨,可以通过定义如下函数来支持这两种模式(接受连接作为参数或使用绑定连接)send-msg:
(defn send-msg [msg & {:keys [connection]
:or {connection *current-connection*}}]
(.sendMessage connection msg))
;; usage with bound connetion:
(with-connection conn
(send-msg "Hello World!"))
;; usage with explicit connection:
(send-msg "Hello World!"
:connection conn)
此版本send-msg使用提供的连接,如果未指定连接,则使用绑定连接。