3

我想知道在 Clojure 中实现数据封装的惯用方法是什么。下面我描述了我的特定用例来激发我提供的示例代码。

我在dbaccess 模块中有代码,它执行复杂的数据库查询以从数据库中获取一些数据。我还有许多对返回的原始数据进行操作的函数。然后,这些函数提供更多处理、更精细的数据视图,并使用来自系统中其他模块的不同参数多次调用。我们称它们为“ API 函数”。

查询很繁重,应该在开始时只执行一次,然后API 函数将对内存中的原始数据进行操作,而无需执行另一个 DB 查询。

这是我使用闭包的方法:

数据库访问模块

(ns dbaccess)
    (let
        [raw-data (complex-database-query)]
      (defn create-client-names []
        (fn [some-args] raw-data))
      (defn create-client-portfolio []
        (fn [some-args] raw-data))
      (defn create-client-stocks []
        (fn [some-args] raw-data)))

其他一些客户端模块

(def client-names (create-client-names))

(doall (map println (client-names "Baltimore")))

我不喜欢必须命名已捕获原始数据的已创建函数。

更重要的是,上面的代码不允许客户端模块在执行之前配置查询的各个方面(例如数据库连接信息)。

另一方面,如果不使用闭包,我将不得不在dbaccess 模块和其他需要调用API 函数的模块之间显式地来回传递原始数据。有没有更好的办法?我应该在dbaccess 模块中使用可变状态吗?

4

2 回答 2

3

我将不得不在 dbaccess 模块和需要调用 API 函数的其他模块之间显式地来回传递原始数据

您应该这样做,明确传递函数需要的数据,因为:

  • 这将导致数据的创建方式和处理方式之间的松散耦合。
  • 阅读时功能会更清晰易懂。
  • 单个功能的测试将很容易,因为您可以轻松地模拟数据。
于 2013-03-22T16:05:25.387 回答
0

let我猜你在这种情况下不需要:

(def ^:private raw-data (promise))

(future (deliver raw-date (complex-database-query))) ;; A. Webb mentioned this

(defn create-client-names []
  (fn [some-args] @raw-data))

...

为什么不create-client-names和其他功能只是

(defn create-client-names [some-args]
  @raw-data)

...

?

而 IMO 最好使用doseq而不是map如果有命令体:

(doseq [name (client-names "Baltimore")]
  (println name))
于 2013-03-22T19:12:25.603 回答