如何有效管理Clojure代码库?

时间:2011-10-18 19:35:31

标签: unit-testing clojure

一位同事和我是Clojure的新手。几个月前我们开始了一个项目,但很快发现我们在处理我们的代码库时遇到了困难 - 通过500 LOC我们基本上不知道从哪里开始调试,当出现问题时(通常是这样)。而不是成对,功能是获取列表,数字,或什么是你。

现在我们正在开始一个新的但相关的项目,并将大量旧代码迁移过来。但我们又一次撞墙了。

我们想知道,我们如何有效地管理Clojure项目,尤其是在我们对现有代码进行更改时?

我们想出了什么:

  • 自由使用单元测试
  • 自由使用前,后条件
  • 功能评论中的非正式类型声明
  • 使用defrecord / defstruct / defprotocol实现数据模型,这将真正简化测试

但后期,前提条件似乎不经常使用。单元测试+评论只会对此有所帮助。似乎Clojure程序员通常不会实现正式的数据模型。

我们是不是得到了Clojure? Clojure程序员如何知道他们的代码是健壮且正确的?

3 个答案:

答案 0 :(得分:9)

简单的可组合抽象

"It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." - Alan J. Perlis

对我来说,所有关于编写简单函数的内容。尝试将每个功能分解为最小的单位,然后使用另一个功能组合它们来完成您需要的工作。你知道你的状态很好,每个功能都可以独立测试。如果你在宏观上过于沉重,那么它可以使这一步更难,因为宏的组成不同。

D.R.Y,说真的,不要重复自己

从一堆命名空间中的良好分解的函数开始;每当我需要其他地方的可组合部分之一时,我“提升”该功能直到两个命名空间包含的库。通过这种方式,您常用的抽象类型会在项目过程中演变为“足够的框架”。除非你真的有离散的可组合抽象,否则很难做到这一点。

答案 1 :(得分:9)

我认为这实际上是一个不断发展的领域--Clojure还没有足够长的时间用于管理大型代码库的所有最佳实践和相关工具。

根据我的经验提出一些建议:

  • 以“自下而上”的方式构建代码 - 通常,您希望构建代码的方式将在文件顶部具有“实用程序”代码(或从另一个名称空间导入)和使用这些实用程序功能的“业务逻辑”代码到文件的末尾。如果这看起来很难做,那么可能暗示您的代码需要进行一些重构。

  • 作为示例进行测试 - clojure中的测试代码非常适用于检查代码的完整性以及文档(例如“此函数期望的参数类型是什么?”)。如果您遇到错误,请参考您的测试来检查您的假设,并编写一些新的测试来清除出错的地方。

  • 保持函数简单并构成它们 - 函数式编程的“single responsibility principle”的扩展类型。我认为在Clojure函数中有超过5-10行是一个主要的代码气味(如果这看起来很极端,请记住,你可以在5-10行Clojure中实现尽可能多的50-100行Java / C#)

  • 注意“必要的习惯” - 当我第一次使用Clojure时,我在Clojure中写了很多伪命令式代码。一个例子是用“dotimes”模拟一个for循环并在一个原子中累积一些结果。这可能是痛苦的 - 它不是惯用的,它令人困惑,通常有一个更聪明,更简单,更不容易出错的功能方式。这需要练习,但从长远来看这是值得的......

  • 在REPL上调试 - 通常当我遇到问题时,在REPL编码是最简单的将其清除的方法。通常这意味着运行较大算法的某些特定部分来检查假设等。

  • 重构常用效用函数 - 您可能会在许多函数中找到一堆常见或结构重复。非常值得将其推广到您可以在其他地方或项目中重复使用的功能或宏 - 这样您就可以更严格地测试它并在多个地方获益。如果你可以一路上到Clojure本身就可以获得奖励积分!如果你做得这么好,那么你的主代码库将非常简洁,因此易于管理,只包含真正特定于域的代码。

答案 2 :(得分:1)

很抱歉,为了挖掘这个老问题,迈克拉和亚瑟的答案非常好,但是我也一直在想,因为我一直在学习Clojure,并且认为我会提到我们如何组织文件。

与确保每个函数只有一个作业类似,我们将相关函数分组到命名空间中,以便更容易地导航代码。因此,我们可能有一个命名空间,用于提供对特定数据库的访问,或提供与HTTP相关的实用程序的集合。这使每个文件相对较小,并使测试更容易找到。它还使重构变得更加直接。这几乎不是什么新鲜事,但值得记住。