当我从处理程序访问 reitit 路由信息时避免循环依赖

Bil*_*oon 1 clojure reitit

假设我设置了某种路由器,将一些路由映射到这样的处理程序......

(ns myapp.user.api
  (:require [reitit.core :as r]))

; define handlers here...

(def router
  (r/router
    [["/user" {:get {:name ::user-get-all
                     :handler get-all-users}}]
     ["/user/:id"
      {:post {:name ::user-post
              :handler user-post}}
      {:get {:name ::user-get
             :handler user-get}}]]))
Run Code Online (Sandbox Code Playgroud)

然后这些处理程序调用想要访问路由信息的服务......

(ns myapp.user-service
  (:require [myapp.user.api :as api]))


; how can I get access to the route properties inside here..?
(defn get-all-users [])
  (println (r/route-names api/router)))
Run Code Online (Sandbox Code Playgroud)

当我尝试将路由器从 api 文件导入服务时,出现循环依赖问题,因为 api 需要处理程序,而该处理程序需要服务,因此服务不能再需要 api。

避免这种循环依赖的最佳方法是什么?我可以从服务中查找路由器的值和属性吗?

Cri*_*ton 5

我使用六种通用方法来避免 clojure 中的循环依赖。它们都有不同的权衡,在某些情况下,一种会比另一种更适合。我按照我最喜欢到我最不喜欢的顺序列出它们。

我在下面为每一个展示了一个例子。可能还有更多我没有想到的方法,但希望这能给你一些思考这个问题的方法。

  1. 重构代码以将常用引用的变量移除到新的命名空间中,并从两个原始命名空间中要求该命名空间。通常这是最好和最简单的方法。但不能在这里完成,因为根处理程序 var 是一个包含来自其他命名空间的 var 的文字。

  2. 在运行时将依赖值传递到函数中,以避免必须逐字地要求命名空间。

(ns circular.a)

(defn make-handler [routes]
  (fn []
    (println routes)))
Run Code Online (Sandbox Code Playgroud)
(ns circular.b
  (:require [circular.a :as a]))

(def routes
  {:handler (a/make-handler routes)})

;; 'run' route to test
((:handler routes))
Run Code Online (Sandbox Code Playgroud)
  1. 使用 multimethods 提供调度机制,然后从其他命名空间定义绑定。
(ns circular.a
  (:require [circular.b :as b]))

(defmethod b/handler :my-handler [_]
  (println b/routes))
Run Code Online (Sandbox Code Playgroud)
(ns circular.b)

(defmulti handler identity)

(def routes
  {:handler #(handler :my-handler)})
Run Code Online (Sandbox Code Playgroud)
(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))
Run Code Online (Sandbox Code Playgroud)
  1. 使用在运行时解析的 var 文字
(ns circular.a)

(defn handler []
  (println (var-get #'circular.b/routes)))
Run Code Online (Sandbox Code Playgroud)
(ns circular.b
  (:require [circular.a :as a]))

(def routes
  {:handler a/handler})

;; 'run' route to test
((:handler routes))
Run Code Online (Sandbox Code Playgroud)
  1. 将代码移动到相同的命名空间中。
(ns circular.a)

(declare routes)

(defn handler []
  (println routes))

(def routes
  {:handler handler})

;; 'run' route to test
((:handler routes))
Run Code Online (Sandbox Code Playgroud)
  1. 使用状态。在运行时将其中一个值存储在原子中。
(ns circular.a
  (:require [circular.c :as c]))

(defn handler []
  (println @c/routes))
Run Code Online (Sandbox Code Playgroud)
(ns circular.b
  (:require [circular.a :as a]
            [circular.c :as c]))

(def routes
  {:handler a/handler})

(reset! c/routes routes)

((:handler routes))
Run Code Online (Sandbox Code Playgroud)
(ns circular.c)

(defonce routes (atom nil))
Run Code Online (Sandbox Code Playgroud)