我使用六种通用方法来避免 clojure 中的循环依赖。它们都有不同的权衡,在某些情况下,一种会比另一种更适合。我按照我最喜欢到最不喜欢的顺序列出它们。
我在下面展示了一个例子。可能还有更多我没有想到的方法,但希望这能给你一些思考这个问题的方法。
重构代码以将常用引用的 var 删除到新的命名空间中,并从两个原始命名空间中都需要该命名空间。通常这是最好和最简单的方法。但不能在这里完成,因为根处理程序 var 是一个包含来自其他命名空间的 var 的文字。
在运行时将依赖值传递给函数,以避免字面上需要命名空间。
(ns circular.a)
(defn make-handler [routes]
(fn []
(println routes)))
(ns circular.b
(:require [circular.a :as a]))
(def routes
{:handler (a/make-handler routes)})
;; 'run' route to test
((:handler routes))
- 使用多方法来提供调度机制,然后从另一个命名空间定义您的绑定。
(ns circular.a
(:require [circular.b :as b]))
(defmethod b/handler :my-handler [_]
(println b/routes))
(ns circular.b)
(defmulti handler identity)
(def routes
{:handler #(handler :my-handler)})
(ns circular.core
(:require [circular.b :as b]
;; now we bring in our handlers so as to define our method implementations
[circular.a :as a]))
;; 'run' route to test
((:handler b/routes))
- 使用在运行时解析的 var 文字
(ns circular.a)
(defn handler []
(println (var-get #'circular.b/routes)))
(ns circular.b
(:require [circular.a :as a]))
(def routes
{:handler a/handler})
;; 'run' route to test
((:handler routes))
- 将代码移动到相同的命名空间中。
(ns circular.a)
(declare routes)
(defn handler []
(println routes))
(def routes
{:handler handler})
;; 'run' route to test
((:handler routes))
- 使用状态。在运行时将其中一个值存储在原子中。
(ns circular.a
(:require [circular.c :as c]))
(defn handler []
(println @c/routes))
(ns circular.b
(:require [circular.a :as a]
[circular.c :as c]))
(def routes
{:handler a/handler})
(reset! c/routes routes)
((:handler routes))
(ns circular.c)
(defonce routes (atom nil))