Lambda和清洁代码的一个重构案例 - frankel


原代码:

List<Person> persons = ...;
persons.stream().filter(p -> {
    if (p.getGender() == Gender.MALE) {
        return true;
    }
    LocalDate now = LocalDate.now();
    Duration age = Duration.between(p.getBirthDate(), now);
    Duration adult = Duration.of(18, ChronoUnit.YEARS);
    return age.compareTo(adult) > 0) {
        return true;
    }
    return false;
}).map(p -> p.getFirstName() + " " + p.getLastName())
  .collect(Collectors.toList());

它很长,甚至令人费解。
第一步是应用正确的命名,并将逻辑移到它所属的位置。

public class Person {

    // ...

    public boolean isMale() {
        return getGender() == Gender.MALE;
    }

    public boolean isAdult(LocalDate when) {
        Duration age = Duration.between(birthDate, when);
        Duration adult = Duration.of(18, ChronoUnit.YEARS);
        return age.compareTo(adult) > 0;
    }
}

这个小的重构已经提高了lambda的可读性:

persons.stream().filter(p -> {
    if (p.isMale()) {
        return true;
    }
    LocalDate now = LocalDate.now();
    return p.isAdult(now);
}).map(p -> p.getFirstName() + " " + p.getLastName())
        .collect(Collectors.toList());

命名lambda:

// Implementation details
Predicate<Person> isMaleOrAdult = p -> {
    if (p.isMale()) {
        return true;
    }
    LocalDate now = LocalDate.now();
    return p.isAdult(now);
};
Function<Person, String> concatenateFirstAndLastName = p -> p.getFirstName() +
" " + p.getLastName();

// Core
persons.streams()
    .filter(isMaleOrAdult)
    .map(concatenateFirstAndLastName)

流(最后一行)变得更具可读性,而不是隐藏在实现细节的后面。它不会阻止开发人员阅读它们,只有在必要时才阅读。