Zacard's Notes

使用java8编写整洁的逻辑判断

背景

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());
    }
}

结论

从长远发展来看,将业务逻辑判断移到一个逻辑判断的辅助类中,将提供以下几个优点:

  • 你的逻辑判断很容易修改和测试
  • 你的对象服务类将保持整洁,让你集中在业务流程而不是业务逻辑判断
  • 提高了代码的重用性,减少了代码的维护成本
  • 进一步分离出了业务的操作关系

以上从这里(传送门)翻译整理。

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章