我想开一个关于Clojure的会话。你能推荐一个可以用Clojure函数式编程优雅地解决的问题吗?你能指出涵盖这个主题的资源吗?
我想举办一场关于Clojure的讨论会。您能否推荐一个可以用Clojure函数式编程优雅地解决的问题?您能指出涵盖此主题的资源吗?我想开一个关于Clojure的会话。你能推荐一个可以用Clojure函数式编程优雅地解决的问题吗?你能指出涵盖这个主题的资源吗?
我想举办一场关于Clojure的讨论会。您能否推荐一个可以用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
,其中包含字段id
,name
,email
,socialSecurityNumber
和其他字段。您正在创建一个Web服务,用于检索数据库中所有人的姓名和电子邮件。您不想暴露您的领域,所以创建了一个类 PersonDto
,其中包含name
和email
。这很容易,现在您需要编写一个函数来将数据从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代码都基于某些状态,即使没有必要也是如此。这意味着您可以模拟这种状态,这通常意味着额外的样板代码,并且如果您没有考虑到测试性而创建代码,则更加困难。另一方面,没有模拟的测试通常很慢,并依赖于数据库访问、时间或其他一些肯定会有时候失败的东西。经典的并发问题“The Sleeping Barber”可能是一个不错的选择。
以下是一些例子:
http://www.bestinclass.dk/index.clj/2009/09/scala-vs-clojure-round-2-concurrency.html
https://github.com/bitsai/clojure-actors/blob/master/sleeping_barber.clj