背景
IN-LINE PREDICATES CAN CREATE A MAINTENANCE NIGHTMARE
代码内部的逻辑判断将会成为代码可维护性的恶梦。
使用lambda表达式和steam接口对集合进行常见的操作是非常畅快的。例如如下代码:
public List<Person> getAdultMales (List<Person> persons) {
return persons.stream().filter(p ->
p.getAge() > ADULT && p.getSex() == SexEnum.MALE
).collect(Collectors.<Person>toList());
}
这很简便!但是这么写却会导致高昂的维护成本。在一个企业级的应用程序中,您的开发团队肯定会有编写重复的业务逻辑判断的代码,这肯定不是你想要看到的项目。因为对于一个易维护,可扩展的企业应用来说,它违反了以下3个重要的原则:
DRY(don`t repeat yourself,不要重复代码):重复一次的代码对于一个“懒惰”的开发者来说就是不好的;同时这也使得您的代码难以维护,因为当业务逻辑改变时,你很难保持业务规则的一致性。
Readability(可读性):根据整洁代码的最佳实践,您编写的80%的代码应该是调用已经存在的代码。相对于简单的一行调用方法来说,lambda表达式仍然是有点难以阅读。
Testability(易测性):您的业务代码需要良好的测试。并且非常建议对复杂的逻辑判断做单元测试。而当你把逻辑判断那部分分离出来后,测试将会异常的简单。
从我个人的角度看,这样的方法仍然包含太多的模板代码。
优化
幸运的是,在单元测试的世界中,有一个非常好的建议能够改变这一问题。
请看如下代码:
import static com.xkeshi.shop.imp.PersonPredicate.*;
/**
* Predicate测试类
*
* @author zacard
* @since 2016-02-01 16:11
*/
public class PredicateTest {
public List<Person> getAdultMales(List<Person> persons) {
return persons.stream().filter(isAdultMale()).collect(Collectors.<Person>toList());
}
}
我们做了什么:
创建了一个PersonPredicate类
定义了一个lambda形式的断言静态工厂方法
静态导入这个工厂方法
PersonPredicate类的定义如下:
/**
* @author zacard
* @since 2016-02-01 17:41
*/
public class PersonPredicate {
public static final int ADULT = 18;
public static Predicate<Person> isAdultMale() {
return p -> p.getAge() > ADULT && p.getSex() == SexEnum.MALE;
}
}
等等,我们为什么不直接在Person类中创建一个叫做“isAdultMale”的boolean函数?这确实是一种选择。。。但是随着时间的推移,项目变的庞大,并且加载越来越多的功能和数据是,你仍然会打破代码整洁的原则:
类的功能和条件变得臃肿
类和单元测试变得巨大,难以处理和修改
添加默认方法
根据面向对象的思想,我们可以想象,一些操作会经常的在对象上被执行(比如过滤操作)。考虑到这点,我们可以让对象服务实现一个接口来定义一个对象的行为,并提供一些默认的方法(java8中允许接口有默认方法的实现),是很有意义的。
例如以下代码:
public interface PersonOperations {
default List<Person> filter(List<Person> persons, Predicate<Person> predicate) {
return persons.stream().filter(predicate).collect(Collectors.toList());
}
}
当我们的person服务实现了这个接口,我们可以让代码更简洁:
import java.util.List;
import static com.xkeshi.shop.imp.predicate.PersonPredicate.isAdultMale;
public class PersonService implements PersonOperations {
public List<Person> getAdultMales(List<Person> persons) {
return filter(persons, isAdultMale());
}
}
结论
从长远发展来看,将业务逻辑判断移到一个逻辑判断的辅助类中,将提供以下几个优点:
- 你的逻辑判断很容易修改和测试
- 你的对象服务类将保持整洁,让你集中在业务流程而不是业务逻辑判断
- 提高了代码的重用性,减少了代码的维护成本
- 进一步分离出了业务的操作关系
以上从这里(传送门)翻译整理。