【问题标题】:Where to put specs for Clojure.Spec?Clojure.Spec 的规范放在哪里?
【发布时间】:2016-10-22 21:18:52
【问题描述】:

所以,我对 Clojure.Spec 的研究越来越深入。

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

全局规范文件

在大多数示例中,我在网上找到了一个大的spec.clj 文件,它在主命名空间中是必需的。它包含所有“数据类型”和函数的所有(s/def)(s/fdef)

亲:

  • 一个文件统统统管

反对:

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

生产命名空间中的规范

您可以将(s/def)(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

专业版:

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

反对:

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

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

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

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

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

    (ns my.app.foo.specs
      (:require [my.app.foo :as f]))
    
    (s/def ::f/name string?)
    

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

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

    【讨论】:

    • @levand Sam Estep 在他的问题中提出了一个很好的观点,即你不能依赖加载的规范来使​​用它进行解析,因为如果它已经需要你的 ns,你就不能需要规范 ns .因此,如果您希望将规范放在单独的文件中以提高可读性,那么没有单独的 ns 的 (load "my/app/foo_specs")/(in-ns 'my.app.foo)(require '[clojure.spec :as s]) 可能是一个更可取的模式
    • 这是一个很好的观点......在某些情况下,您希望您的实现命名空间依赖于您的规范命名空间,这意味着您的规范 ns cannot 依赖于 impl ns .目前,没有很好的方法来解决这个问题——最好的办法就是在规范中输入完全限定的命名空间名称。幸运的是,作品中有一些更改允许您为尚未加载/需要的命名空间设置别名,这应该可以巧妙地解决这个问题。
    • 我同意在同一个文件中的规范存在可读性成本,但是在该命名空间的主文件之外的命名空间中定义事物会产生不同的、可能更糟糕的可读性成本。在主文件中没有任何 requireuse 的情况下,名称变得神奇可用。始终使用规范执行此操作的标准化做法是可以的,但是查看您的代码的局外人仍然可能会感到困惑。 (这个局外人可能一两年后就是你了。) 突出的警告似乎是必要的。正常使用命名空间系统的美妙之处在于不需要这样的 cmets。
    • @levand:“幸运的是,作品中有一些更改将允许您为尚未加载/尚未需要的命名空间命名”请详细说明。
    【解决方案2】:

    在我看来,规范应该与代码在同一 ns 中。

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

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

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

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

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

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

    【讨论】:

    • 我想我同意这一点。关于规范的基本原理文章提到文档是其中的一个方面。如果你把它们放在远离他们规范的代码的地方,你就失去了这种好处。
    • 我也更喜欢这个,除了在 clojure 后端和 clojurescript 前端之间共享的规范之外,我倾向于放入单独的 cljc 文件,而不是将所有命名空间都写为 cljc。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-23
      • 2020-09-03
      • 1970-01-01
      • 2019-07-22
      • 1970-01-01
      • 1970-01-01
      • 2019-11-02
      • 1970-01-01
      相关资源
      最近更新 更多