53

因此,我对Clojure.Spec的研究越来越深入。

我偶然发现的一件事是,把我的规格放在哪里。我看到三个选项:

全球规格文件

在大多数示例中,我在网上发现,spec.clj主命名空间中需要一个大文件。它具有所有“数据类型”(s/def)(s/fdef)功能。

临:

  • 一个文件来统治他们

反对:

  • 这个文件可能很大
  • 违反单一责任原则?

生产命名空间中的规范

你可以把你的(s/def)and放在你(s/fdef)的生产代码旁边。因此,实现和规范共存于同一个命名空间中。

临:

  • 实施和规范的协同定位
  • 一个命名空间 - 一个问题?

反对:

  • 生产代码可能会变得混乱
  • 一个命名空间 - 两个问题?

专用规范命名空间结构

然后我想,也许 Specs 是第三种代码(在生产和测试之后)。所以也许他们应该拥有自己的命名空间结构,如下所示:

├─ src
│  └─ package
│     ├─ a.clj
│     └─ b.clj
├─ test
│  └─ package
│     ├─ a_test.clj
│     └─ b_test.clj
└─ spec
   └─ package
      ├─ a_spec.clj
      └─ b_spec.clj

临:

  • 规范的专用(但相关)命名空间

反对:

  • 您必须获取并要求正确的命名空间

谁有使用其中一种方法的经验?
还有其他选择吗?
您如何看待不同的选择?

4

3 回答 3

22

我通常将规范放在他们自己的名称空间中,以及他们所描述的名称空间中。只要它们使用一些一致的命名约定,它们的名称并不特别重要。例如,如果我的代码在 中my.app.foo,我会将规范放在my.app.foo.specs.

规范键名最好位于代码的命名空间中,而不是规范的命名空间。通过在关键字上使用命名空间别名仍然很容易做到这一点:

(ns my.app.foo.specs
  (:require [my.app.foo :as f]))

(s/def ::f/name string?)

我不会尝试将所有内容放在一个巨大的规范命名空间中(真是一场噩梦。)虽然我当然可以将它们与规范的代码放在同一个文件中,但这会损害 IMO 的可读性。

可以将所有规范命名空间放在单独的源路径中,但这样做并没有真正的好处,除非您处于想要分发代码而不是规范的情况,反之亦然......很难想象那是什么不过会。

于 2016-06-21T13:27:05.023 回答
16

在我看来,规范应该与代码相同。

我的主要考虑是我的个人哲学,从技术上讲,它运作良好。我的理念是函数的规范是它不可或缺的一部分。这不是多余的东西。这绝对不是 OP 中提出的“两个问题”。实际上,它就是函数,因为就正确性而言:谁在乎实现?谁在乎你写了什么defn?谁在乎标识符?除了规格,谁在乎?
我认为这很奇怪,因为不仅 clojure.spec 是后来出现的,而且大多数语言也不会让你将规范作为一个不可或缺的东西,即使你希望它是,而且任何接近的东西(可能是代码中的测试)通常都会不受欢迎,所以当然,这看起来很奇怪。但是考虑一下,您可能会像我一样得出结论(或者您可能不会,这部分是自以为是的)。

关于为什么您不希望在同一个 ns 中使用规格,我能想到的唯一充分理由是以下两个原因:

  1. 它使您的代码混乱。
  2. 您想支持 Clojure 1.9.0 之前的 Clojure 版本。

至于第一个原因,好吧,我认为它是你的职能不可分割的一部分。如果你发现它真的太多了,我的建议就像你的 ns 太杂乱,不管规格如何:看看你能不能把它分开。

至于第二个原因,如果您真的关心,您可以检查 clojure.spec ns 是否可用,如果不可用,则使用 NOP 的函数/宏隐藏名称。另一种选择是使用clojure-future-spec但我自己没有尝试过,所以我不知道它的效果如何。

另一种在技术上效果很好的方法是,有时存在您无法使用不同名称空间处理的循环依赖关系,例如,当您的规范依赖于您的代码(对于函数规范)并且您的代码依赖于您的解析规范时(参见这个问题)。

于 2017-01-04T10:43:47.390 回答
8

取决于您的用例的另一个考虑因素 - 将规范与您的主要代码一起限制使用您的代码到 Clojure 1.9 客户端,这可能是您想要的,也可能不是您想要的。与@levand 一样,我会为您的每个代码命名空间推荐一个并行命名空间。

于 2016-06-21T22:45:17.647 回答