clojure - Clojure 的良好展示是什么?

标签 clojure

我想举办一个有关 Clojure 的 session 。您能否推荐一个可以使用 Clojure 函数式编程优雅解决的问题?您能指出涵盖该主题的资源吗?

最佳答案

很多关于使用 Clojure 的争论似乎都与其并发处理有关,但我在这里不会触及这个问题。

我将列出一些我每周都不得不用 Java 处理的问题,以及我如何在 Clojure 中解决这些问题。

不变性

在 Java 中实现不变性是非常非常困难的。除了遵循严格的编码实践之外,您还必须非常仔细地选择框架和库。另外一个副作用是,您要么编写大量代码来创建干净且可用的 API,要么只是强制客户端处理它。

final Person person = personDao.getById(id);
// I would like to "change" the person's email, but no setters... :(

在 Clojure 中,您基于不可变数据结构对数据进行建模,因此默认情况下所有对象都是不可变的,因此 Clojure 提供了对这些结构进行操作的强大函数。

(let [person            (get-by-id person-dao id)
      person-with-email (assoc person :email email)]
  ; Use person-with-email...

转化

在 Java 中,您有一个域类 Person,其中包含字段 idnameemail社会安全号 等。您正在创建一个 Web 服务,用于检索数据库中所有人员的姓名和电子邮件。您不想公开您的域,因此创建了一个包含 nameemailPersonDto 类。这很简单,所以现在您需要一个函数将数据从 Person 映射到 PersonDto。大概是这样的:

public class PersonPopulator {
    public PersonDto toPersonDto(Person person) {
        return new PersonDto(person.getName(), person.getEmail());
    }

    public List<PersonDto> toPersonDtos(List<Person> persons) {
        List<PersonDto> personDtos = new ArrayList<PersonDto>();
        for (Person person : persons) {
            personDtos.add(toPersonDto(person));
        }
        return personDtos;
    }
}

好吧,这还不错,但是如果您想在 DTO 中放入更多数据怎么办?嗯,toPersonDto 中的构造函数代码会增加一点,不用担心。如果有两种不同的用例,一种是上面的情况,另一种是我们只想发送电子邮件的情况,该怎么办?好吧,我们可以将 name 保留为空(坏主意)或创建一个新的 DTO,也许是 PersonWithEmailDto。因此,我们将创建一个新类,一些用于填充数据的新方法......您可能知道这是怎么回事?

Clojure 是一种具有不可变数据结构的动态类型语言,允许我执行此操作:

(defn person-with-fields [person & fields]
  (reduce #(assoc %1 %2 (get person %2)) {} fields))

(person-with-fields {:id 1 
                     :name "John Doe"
                     :email "john.doe@gmail.com"
                     :ssn "1234567890"} :name :email)
; -> {:email "john.doe@gmail.com", :name "John Doe"}

并操纵人员列表:

(map #(person-with-fields % :name :email) persons)

向某人添加临时数据也很容易:

(assoc person :tweets tweets)

这不会破坏任何东西。在 Java 中,如果你的对象是不可变的,它们可能没有 setter ,所以你必须编写大量样板文件才能修改一个字段(new Person(oldPerson.getName(), oldPerson.getEmail(), tweets )),或者创建一个全新的类。可变对象提供了一个很好的 API (oldPerson.setTweets(tweets)),但难以测试和理解。

测试

许多 Java 代码都基于某种状态,即使不需要它。这意味着您可以模拟此状态,这通常意味着额外的样板文件,如果您没有在创建代码时考虑到可测试性,那么这会变得更加困难。另一方面,没有模拟的测试通常很慢,并且依赖于数据库访问或时间或其他肯定会不时失败的东西。

在编写 Clojure 代码时,我注意到我实际上并不需要那么多状态。几乎唯一的情况是当我从“外部”检索某些内容时,无论是数据库还是某些 Web 服务。

摘要

我的代码是一个管道,从一端获取一些数据,然后通过过滤、转换或与其他数据连接来更改该数据,直到到达管道的末端。在管道内部,不需要真正更改数据,但在很多情况下,强大的函数和不可变的数据结构很有用,这就是为什么 Clojure 使用这样的代码会产生奇迹。

关于clojure - Clojure 的良好展示是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5353691/

相关文章:

clojure - 将 Java 对象转换为 Clojure 类型

clojure - Clojure 的 'into' 是否有效?

java - 火狐未连接异常

clojure - 嵌套级别之间具有相互依赖值的 map 规范?

宏,clojure vs common lisp

clojure - 是否可以使用 main 方法创建 clojure 类型? (使用deftype,而不是gen-class)

jQuery 附加的 HTML 元素不会使用 crate 和 clj-js 获取 CSS 样式

clojure - 打ic 1.0.0-beta1错误

clojure - 我可以在 Clojure 中对 Java 方法调用进行部分处理吗?

oop - 将面向对象模型映射到 Clojure