我的公司刚刚要求我在 Clojure 中重写一个较大的(50,000 行代码)Java 应用程序(一个使用 JSP 和 servlet 的 Web 应用程序)。有其他人告诉我应该注意什么吗?
请记住,我非常了解 Java 和 Clojure。
更新
我重写了它并投入生产。这很奇怪,因为重写速度如此之快,大约六周就完成了。因为不需要很多功能,所以它最终更像是 3000 行 Clojure。
我听说他们对这个系统很满意,而且它完全按照他们的意愿行事。唯一的缺点是维护系统的人必须从头开始学习Clojure,他被拖进去又踢又叫。前几天我确实接到他的电话,说他现在喜欢 Lisp。有趣的是:)
另外,我应该好好提一下 Vaadin。使用 Vaadin 可能与 Clojure 一样节省了时间并缩短了代码。 Vaadin 仍然是我用过的顶级 Web 框架,尽管现在我正在愤怒地学习 ClojureScript! (请注意,Vaadin 和 ClojureScript 都在底层使用 Google 的 GUI 框架。)
I have just been asked by my company to rewrite a largish (50,000 single lines of code) Java application (a web app using JSP and servlets) in Clojure. Has anyone else got tips as to what I should watch out for?
Please bear in mind that I know both Java AND Clojure quite well.
Update
I did the rewrite and it went into production. It's quite strange as the rewrite ended up going so fast that it was done in about 6 weeks. Because a lot of functionality wasn't needed still it ended up more like 3000 lines of Clojure.
I hear they are happy with the system and its doing exactly what they wanted. The only downside is that the guy maintaining the system had to learn Clojure from scratch, and he was dragged into it kicking and screaming. I did get a call from him the other day saying he loved Lisp now though.. funny :)
Also, I should give a good mention to Vaadin. Using Vaadin probably accounted for as much of the time saved and shortness of the code as Clojure did.. Vaadin is still the top web framework I have ever used, although now I'm learning ClojureScript in anger! (Note that both Vaadin and ClojureScript use Google's GUI frameworks underneath the hood.)
发布评论
评论(3)
最大的“转换问题”可能是从 Java / OOP 方法到 Clojure / 函数式编程范例。
特别是,“Clojure 方式”不是在对象内具有可变状态,而是明确分离出可变状态并开发纯(无副作用)函数。您可能已经知道这一切了:-)
无论如何,这种哲学往往会导致某种“自下而上”的开发风格,您将最初的努力集中在构建正确的工具集来解决您的问题,然后最终将它们组合在一起结束。这可能看起来像这样
识别关键数据结构并将它们转换为不可变的 Clojure 映射或记录定义。不要害怕嵌套大量不可变映射 - 由于 Clojure 的持久数据结构,它们非常高效。值得观看此视频以了解更多信息。
开发纯的、面向业务逻辑的函数的小型库,这些函数在这些不可变的结构上运行(例如“将商品添加到购物车”)。您不需要立即执行所有这些操作,因为稍后添加更多内容很容易,但早期执行一些操作有助于促进测试并证明您的数据结构正在工作......无论哪种方式实际上,您可以开始在 REPL 上以交互方式编写有用的内容
单独开发数据访问例程,这些例程可以根据需要将这些结构持久保存到数据库或网络或遗留 Java 代码中/从数据库或网络或遗留 Java 代码中保存。保持这一点非常独立的原因是您不希望持久性逻辑与您的“业务逻辑”功能捆绑在一起。您可能需要查看 ClojureQL 来了解这一点,尽管包装您所使用的任何 Java 持久性代码也很容易
编写涵盖以下内容的单元测试(例如使用clojure.test)以上所有。这对于像 Clojure 这样的动态语言来说尤其重要,因为 a) 你没有太多静态类型检查的安全网,b) 它有助于确保你的较低层结构在你构建太多内容之前运行良好。 编写单元测试
选择正确的整体 Web 框架 - Clojure 已经有很多,但我强烈推荐 Ring - 请观看这个精彩的视频“一环来绑定它们"加上 Fleet 或 Enlive 或 打嗝取决于您的模板理念。然后使用它来编写您的表示层(具有诸如“将此购物车转换为适当的 HTML 片段”之类的功能)
最后,使用上述工具编写您的应用程序。如果您正确完成了上述步骤,那么这实际上会很容易,因为您将能够通过适当组合各种组件来构建整个应用程序,而只需很少的样板。
这大致是我解决问题的顺序,因为它广泛地代表了代码中依赖关系的顺序,因此适合“自下而上”的开发工作。当然,在良好的敏捷/迭代风格中,您可能会发现自己很早就推进了可演示的最终产品,然后经常跳回早期步骤以根据需要扩展功能或重构。
ps 如果您确实遵循上述方法,我会很想知道需要多少行 Clojure 才能匹配 50,000 行 Java 的功能
更新:由于这篇文章最初是由几行代码编写的已经出现了“必须检查”类别中的额外工具/库:
The biggest "translational issue" will probably be going from a Java / OOP methodology to a Clojure / functional programming paradigm.
In particular, instead of having mutable state within objects, the "Clojure way" is to clearly separate out mutable state and develop pure (side-effect free) functions. You probably know all this already :-)
Anyway, this philosophy tends to lead towards something of a "bottom up" development style where you focus the initial efforts on building the right set of tools to solve your problem, then finally plug them together at the end. This might look something like this
Identify key data structures and transform them to immutable Clojure map or record definitions. Don't be afraid to nest lots of immutable maps - they are very efficient thanks to Clojure's persistent data structures. Worth watching this video to learn more.
Develop small libraries of pure, business logic oriented functions that operate on these immutable structures (e.g. "add an item to shopping cart"). You don't need to do all of these at once since it is easy to add more later, but it helps to do a few early on to facilitate testing and prove that your data structures are working..... either way at this point you can actually start writing useful stuff interactively at the REPL
Separately develop data access routines that can persist these structures to/from the database or network or legacy Java code as needed. The reason to keep this very separate is that you don't want persistence logic tied up with your "business logic" functions. You might want to look at ClojureQL for this, though it's also pretty easy to wrap any Java persistence code that you like.
Write unit tests (e.g. with clojure.test) that cover all the above. This is especially important in a dynamic language like Clojure since a) you don't have as much of a safety net from static type checking and b) it helps to be sure that your lower level constructs are working well before you build too much on top of them
Decide how you want to use Clojure's reference types (vars, refs, agents and atoms) to manage each part mutable application-level state. They all work in a similar way but have different transactional/concurrency semantics depending on what you are trying to do. Refs are probably going to be your default choice - they allow you to implement "normal" STM transactional behaviour by wrapping any code in a (dosync ...) block.
Select the right overall web framework - Clojure has quite a few already but I'd strongly recommend Ring - see this excellent video "One Ring To Bind Them" plus either Fleet or Enlive or Hiccup depending on your templating philosophy. Then use this to write your presentation layer (with functions like "translate this shopping cart into an appropriate HTML fragment")
Finally, write your application using the above tools. If you've done the above steps properly, then this will actually be the easy bit because you will be able to build the entire application by appropriate composition of the various components with very little boilerplate.
This is roughly the sequence that I would attack the problem since it broadly represents the order of dependencies in your code, and hence is suitable for a "bottom up" development effort. Though of course in good agile / iterative style you'd probably find yourself pushing forward early to a demonstrable end product and then jumping back to earlier steps quite frequently to extend functionality or refactor as needed.
p.s. If you do follow the above approach, I'd be fascinated to hear how many lines of Clojure it takes to match the functionality of 50,000 lines of Java
Update: Since this post was originally written a couple of extra tools/libraries have emerged that are in the "must check out" category:
您当前的项目包含 Java 的哪些方面?日志记录、数据库事务、声明式事务/EJB、Web 层(您提到了 JSP、servlet)等。我注意到 Clojure 生态系统有各种微框架和库,其目标是完成一项任务,并把它做好。我建议根据您的需求(以及它是否会在大型项目中扩展)评估库并做出明智的决定。 (免责声明:我是 bitumenframework 的作者)另一件事需要注意的是构建过程 -如果您需要复杂的设置(开发、测试、登台、生产),您可能必须将项目拆分为模块,并编写构建过程脚本以方便使用。
What aspects of Java does your current project include? Logging, Database transactions, Declarative transactions/EJB, web layer (you mentioned JSP, servlets) etc. I have noticed the Clojure eco-system has various micro-frameworks and libraries with a goal to do one task, and do it well. I'd suggest evaluate libraries based on your need (and whether it would scale in large projects) and make an informed decision. (Disclaimer: I am the author of bitumenframework) Another thing to note is the build process - if you need a complex setup (dev, testing, staging, prod) you may have to split the project into modules and have the build process scripted for ease.
我发现最困难的部分是考虑数据库。进行一些测试以找到您想要在那里使用的正确工具。
I found the most difficult part was thinking about the database. Do some tests to find the right tools you want to use there.