14

我记得读过一篇关于 Ruby 不需要 DI 或 DI 框架的文章,因为这些类是开放的。因此,您可以简单地重写依赖项的构造函数,以便它返回一个假对象。

我对 Clojure 和函数式编程非常陌生。我想知道 Clojure 是否需要依赖注入,或者它可以出于类似/其他原因放弃它。这是一个可以使用的具体示例(请随意指出我的设计如何不符合 Clojure 的习惯):

想象一下,您正在开发一个网络爬虫/蜘蛛。它需要遍历您下载的网页。这是一个有副作用的动作。网页可能会在每次查询时发生变化,您的互联网连接可能会中断等。它会找到网页上的所有链接,访问每个链接,然后以相同的方式遍历它。

现在,您想编写一个模拟 http 客户端的测试,以便它返回一个硬编码的字符串响应。你如何-main在测试中调用程序并阻止它使用真正的http客户端?

4

3 回答 3

17

clojure.core 中的with-redefs宏对于存根函数非常有用。

这是一个简短的 REPL 会话来演示这一点:

user=> (defn crawl [url]
  #_=>   ;; would now make http connection and download content
  #_=>   "data from the cloud")
#'user/crawl
user=> (defn -main [& args]
  #_=>   (crawl "http://www.google.com"))
#'user/-main
user=> (-main)
"data from the cloud"
user=> (with-redefs [crawl (fn [url] "fake data")]
  #_=>   (-main))
"fake data"

由于 Clojure 程序(大部分)由函数而非对象组成,因此函数的动态重新绑定取代了 DI 框架为测试目的所做的大量工作。

于 2013-03-29T04:25:37.423 回答
11

在 Clojure 中,您通常可以使用替代方法实现依赖注入的等价物:

  • 动态绑定- 例如,通过执行重定向测试函数中的标准输出很有用(binding [*out* some-writer-object] ...)
  • 高阶函数- 可用于通过将其他函数作为参数传递来提供一种形式的依赖注入
  • 使用数据进行配置- 在 Clojure 中传递包含配置参数(甚至是配置自定义行为的函数)的映射是相当惯用的。

所有这些都是语言本身不可或缺的。所以你绝对不需要像“DI 框架”这样的东西。恕我直言,需要一个 DI 框架实际上只是弥补了语言本身缺乏足够的特性。

于 2013-03-29T03:41:30.760 回答
3

想象一下,您正在开发一个网络爬虫/蜘蛛。它需要遍历您下载的网页。这是一个有副作用的动作。网页可能会在每次查询时发生变化,您的互联网连接可能会中断等。它会找到网页上的所有链接,访问每个链接,然后以相同的方式遍历它。

我认为这里不需要依赖注入。以下是我认为在保持可测试性的同时解决这个问题的方法:将实现分离成至少两个函数。一个函数获取并返回网页,另一个函数解析响应。在您的主要功能中,您将拥有类似(-> "http://google.com" fetch parse).

然后,为了测试parse,您可以简单地绕过该fetch方法并将虚假网页数据直接输入该parse方法。

(deftest test
     (is (= {:something "blah"} (parse "<html><head><title>Fake webpage etc..</title></head></html>"))))

只要你小心地将问题分解为清晰的函数,我认为你不需要像 DI 这样复杂的东西来测试。

我是 Clojure 的新手,所以我不熟悉这里其他人提到的宏和技术,但到目前为止,上述方法对我很有帮助。

于 2013-03-29T18:25:41.823 回答