Java 到 Clojure 重写

Posted

技术标签:

【中文标题】Java 到 Clojure 重写【英文标题】:Java to Clojure rewrite 【发布时间】:2011-07-11 02:22:23 【问题描述】:

我的公司刚刚要求我在 Clojure 中重写一个大型(50,000 行代码)Java 应用程序(一个使用 JSP 和 servlet 的 Web 应用程序)。有其他人知道我应该注意什么吗?

请记住,我非常了解 Java 和 Clojure。

更新

我进行了重写并投入生产。这很奇怪,因为重写结束得如此之快,以至于在大约 6 周内就完成了。因为仍然不需要很多功能,所以它最终更像是 3000 行 Clojure。

我听说他们对这个系统很满意,而且它完全按照他们的意愿行事。唯一的缺点是维护系统的人必须从头开始学习 Clojure,他被拖入其中又踢又叫。前几天我确实接到了他的电话,说他现在喜欢 Lisp.. 有趣:)

另外,我应该好好提一下 Vaadin。使用 Vaadin 可能节省了与 Clojure 一样多的时间和代码的简洁性。Vaadin 仍然是我曾经使用过的*** Web 框架,尽管现在我正在愤怒地学习 ClojureScript! (请注意,Vaadin 和 ClojureScript 都在底层使用 Google 的 GUI 框架。)

【问题讨论】:

我想为贵公司工作。 嗯,欧洲的一些国防公司已经开始使用 Clojure(我听说了)。但记不起任何名字:) @Zubair:50 000 Java LOC 并不接近“大”。这是一个非常小的项目。我这里有一个 250KLOC 到 300KLOC 的 Java 项目,它是中等规模的......充其量。 实际上我可能犯了一个错误,提到代码库的大小是公平的,减少代码大小不是重写的目的。目的是双重的:1) 让代码库更易于理解,从而更便宜地维护代码 2) 允许该工具的用户使用 Web 浏览器中的 Clojure 编辑器来扩展产品(使用 eval 和其他动态的 Clojure 优点,这些优点对用Java做) 嘿...刚看到这个老问题,想知道重写的进展如何? 【参考方案1】:

最大的“翻译问题”可能是从 Java/OOP 方法到 Clojure/函数式编程范式。

特别是,“Clojure 方式”不是在对象中具有可变状态,而是清楚地分离出可变状态并开发纯(无副作用)函数。你可能已经知道这一切了:-)

无论如何,这种理念往往会导致某种“自下而上”的开发风格,在这种风格中,您将最初的努力集中在构建正确的工具集以解决您的问题,然后最终将它们组合在一起。这可能看起来像这样

    识别关键数据结构并将它们转换为不可变的 Clojure 映射或记录定义。不要害怕嵌套大量不可变的映射 - 由于 Clojure 的持久数据结构,它们非常高效。值得关注this video以了解更多信息。

    开发在这些不可变结构上运行的纯面向业务逻辑的函数的小型库(例如,“将商品添加到购物车”)。您不需要一次完成所有这些操作,因为以后添加更多内容很容易,但它有助于尽早做一些以促进测试并证明您的数据结构正在工作......无论哪种方式点你实际上可以在 REPL 上以交互方式开始编写有用的东西

    单独开发数据访问例程,这些例程可以根据需要在数据库或网络或遗留 Java 代码之间保存这些结构。保持这个非常独立的原因是您不希望持久性逻辑与您的“业务逻辑”功能捆绑在一起。你可能想看看ClojureQL,尽管它也很容易包装任何你喜欢的Java持久性代码。

    编写涵盖以上所有内容的单元测试(例如使用clojure.test)。这在像 Clojure 这样的动态语言中尤其重要,因为 a) 您没有那么多来自静态类型检查的安全网,并且 b) 它有助于确保您的较低级别的结构在您构建太多之前运行良好最重要的

    决定您希望如何使用 Clojure 的引用类型(变量、引用、代理和原子)来管理每个部分的可变应用程序级状态。它们都以类似的方式工作,但根据您要执行的操作具有不同的事务/并发语义。 Refs 可能会成为您的默认选择——它们允许您通过将任何代码包装在 (dosync ...) 块中来实现“正常”的 STM 事务行为。

    选择正确的整体 Web 框架 - Clojure 已经有很多,但我强烈推荐 Ring - 观看这个出色的视频“One Ring To Bind Them”加上Fleet 或Enlive 或Hiccup 取决于关于你的模板理念。然后使用它来编写您的表示层(具有“将此购物车转换为适当的 html 片段”之类的功能)

    最后,使用上述工具编写您的应用程序。如果您正确地完成了上述步骤,那么这实际上会很容易,因为您将能够通过适当组合各种组件而使用非常少的样板来构建整个应用程序。

这大致是我要解决问题的顺序,因为它广泛地代表了代码中依赖项的顺序,因此适合“自下而上”的开发工作。当然,在良好的敏捷/迭代风格中,您可能会发现自己很早就推向可演示的最终产品,然后经常跳回到较早的步骤以扩展功能或根据需要进行重构。

附言如果您确实遵循上述方法,我会很想知道需要多少行 Clojure 才能匹配 50,000 行 Java 的功能

更新:由于这篇文章最初是写的,所以出现了一些额外的工具/库,它们属于“必须检查”类别:

Noir - 基于 Ring 构建的网络框架。 Korma - 一个非常好的访问 SQL 数据库的 DSL。

【讨论】:

Nitpick re:“决定要使用 Clojure 的哪种 STM 引用类型来管理每个部分的可变应用程序级状态”:只有一种 STM 引用类型。其他 IRef 不涉及 STM。否则看起来像是可靠的建议。 hmmm... 我想我把 refs、agents 和 atom 都算作 Clojure STM/并发系统的一部分。例如,它们都支持验证器,并且代理与事务的提交协调。但我明白你的意思,参考是“主要”交易模型。将进行快速修改。 如果我能给这个更多+1。我看了你提到的两个视频,它们都很棒。谢谢。 现在不推荐使用黑色。我想你不得不提到 compojure 而不是 noir。 一环绑定他们链接坏了!【参考方案2】:

您当前的项目包括 Java 的哪些方面?日志记录、数据库事务、声明式事务/EJB、Web 层(你提到了 JSP、servlet)等。我注意到 Clojure 生态系统有各种微框架和库,目标是完成一项任务,并且做得很好。我建议根据您的需要评估库(以及它是否会在大型项目中扩展)并做出明智的决定。 (免责声明:我是bitumenframework 的作者)另外需要注意的是构建过程 - 如果您需要复杂的设置(开发、测试、登台、产品),您可能必须将项目拆分为模块并进行构建过程为方便而编写脚本。

【讨论】:

Servlets、JSP、自制的持久性框架(10 岁)和 pojos,但没有 EJB Servlet 可以很容易地替换为 Ring+Compojure 恕我直言,而 JSP 可以替换为 StringTemplate(或 FreeMarker/Velocity 模板)。Clojure 中的持久性将不同于 Java。如果需要关系映射,可以看看 Clj-Record 和 SQLRat(还不是很成熟)。 ClojureQL 目前仅支持 mysql 和 PostgreSQL AFAICT。当前 c.c.sql 中不允许在列名中使用下划线的限制可能会让人感到意外。我认为在需要时讨论 Clojure 列表中的开发方面会很有用。祝项目好运! 从那个项目开始,我用 Clojure 和 Clojurescript (nemcv.com) 制作了另一个应用程序,现在我正在使用 Ring,尝试了 ClojureQL,但切换到 Korma 进行数据库访问。你可以在github.com/zubairq/coils看到最新作品【参考方案3】:

我发现最困难的部分是考虑数据库。进行一些测试以找到您想在其中使用的正确工具。

【讨论】:

好吧,我已经尝试使用 clojureql 进行数据访问,但它与完全基于对象的 Java 数据库访问风格完全不同。只是出于兴趣,您对 Java 使用了哪些数据库访问,以及您对 Clojure 使用了什么? 我们最终重新考虑了很多东西并选择了 mongodb,因为 clojure 数据结构的持久性非常自然

以上是关于Java 到 Clojure 重写的主要内容,如果未能解决你的问题,请参考以下文章

将 java 实例正确绑定到 Vars 的 clojure

使用clojure.java.jdbc在Clojure中使用外键约束

如何使用 clojure.java.jdbc 进行 UPSERT

线程“main”中的异常 java.lang.ExceptionInInitializerError (Clojure)

Clojure 运行原理之字节码生成篇

Clojure : 类型提示塔