避免 Clojure 应用程序的 DAO 层中的全局状态

Posted

技术标签:

【中文标题】避免 Clojure 应用程序的 DAO 层中的全局状态【英文标题】:Avoiding global state in DAO layer of a Clojure application 【发布时间】:2013-06-05 04:10:48 【问题描述】:

我正在尝试将http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded 的想法实施到我的代码库中。

我有一个 dao 层,我现在需要传入一个数据库以避免全局状态。让我失望的一件事是这句话:

任何需要这些组件之一的功能都必须将其视为 范围。这并不像看起来那么繁琐:每个函数 最多得到一个额外的参数,提供它所在的“上下文” 运作。该上下文可能是整个系统对象,但更多 通常会是一些子集。通过明智地使用词法闭包, 额外的参数从大多数代码中消失了。

我应该在哪里使用闭包以避免每次调用都传递全局状态?一个示例是在 dao 层中创建一个 init 函数,如下所示:

(defprotocol Persistable
  (collection-name [this]))

(def save nil)

(defn init [:keys [db]]
  (alter-var-root #'save (fn [_] (fn [obj] (mc/insert-and-return db (collection-name obj) obj WriteConcern/SAFE)))))

这样我可以像这样从系统/启动函数启动我的 dao 层:

(defn start
  [:keys [db] :as system]
  (let [d (-> db
              (mc/connect)
              (mc/get-db "my-test"))]
    (dao/init d)
    (assoc system :db d)))

这行得通,但感觉有点恶心。有没有更好的办法?如果可能的话,我想避免强制我的 dao 层的客户端每次使用函数时都必须传递数据库。

【问题讨论】:

【参考方案1】:

您可以使用高阶函数来表示您的 DAO 层 - 这是函数式编程的关键,使用函数来表示系统的小到大部分。所以你有一个更高阶的函数,它将数据库连接作为参数并返回另一个函数,你可以使用它来调用数据库上的各种操作,如保存、删除等。下面是一个这样的例子:

(defn db-layer [db-connection]
  (let [db-operations :save (fn [obj] (save db-connection obj))
                       :delete (fn [obj] (delete db-connection obj))
                       :query (fn [query] (query db-connection query))]
    (fn [operation & params]
      (-> (db-operations operation) (apply params)))))

DB层的使用:

(let [my-db (create-database)
      db-layer-fn (db-layer my-db)]
  (db-layer-fn :save "abc")
  (db-layer-fn :delete "abc"))

这只是高阶函数如何允许您为另一组函数创建上下文的示例。您可以通过将其与其他 Clojure 功能(如协议)相结合来进一步推广此概念。

【讨论】:

以上是关于避免 Clojure 应用程序的 DAO 层中的全局状态的主要内容,如果未能解决你的问题,请参考以下文章

服务层和 DAO 层中的 Spring 事务

服务层和 dao 层中的事务性

Spring Web 开发中的通用 DAO

Clojure 中的 CSV 解析器需要避免引号中的逗号

道层中的多种方法

java的mvc模式中bean.dao.service三层中都放啥东西啊?能具体说说吗?