我们在之前的项目中实现了CourseSearch类;该类保存了多个查询条件,并提供了成员方法isSatisfied()来检查课程对象是否满足查询条件。但是,当前的CourseSearch类只能按照逻辑"与"来设置条件。比如,用户可以设置条件A和条件B来查询课程。findCourse()方法能够返回同时满足这两个条件的课程。可是,有时用户可能希望给定两个条件,查询结果只需满足其中一个条件即可。我们称这种查询方式为按照逻辑"或"来查询。因此,我们将在本项目中开发一个高级课程搜索功能,允许用户任意使用逻辑"与"和逻辑"或"来组合多个条件。虽然java.util.function.Predicate类已支持了类似的功能,但是本项目作为一个学习项目,我们将使用组合模式来实现这个高级查询功能。
首先,我们在com.littlewaterdrop.search包中声明一个新的公有接口SearchCriterion。它有一个公有方法isSatisfied()。该方法接受一个输入参数Course,返回一个boolean类型的结果。当输入参数满足查询条件时,返回true;当输入参数不满足查询条件时,返回false。
boolean isSatisfied(Course course);
在第二步中,我们实现一个按照课程名字查询的条件。在com.littlewaterdrop.search包中,我们创建一个新类NameBasedCriterion,它实现了SearchCriterion接口。
NameBasedCriterion类声明了一个公有构造函数,接受一个待查询的名字。
NameBasedCriterion类还实现了isSatisfied()方法。当课程名称与构造函数传入的名称相同时,isSatisfied()方法返回true;否则,返回false。
我们再在com.littlewaterdrop.search包中创建一个新的公有类SearchCriterionFactory。它有一个公有静态方法newNameBasedCriterion()方法,根据输入参数name,创建一个NameBasedCriterion类对象。这个静态方法的原型如下所示。
public static NameBasedCriterion newNameBasedCriterion(String name);
我们再实现一个按照逻辑"与"处理的组合查询条件。在com.littlewaterdrop.search包中创建一个新类CombineTwoCriteria,实现接口SearchCriterion。
CombineTwoCriteria类提供一个构造函数,接收两个SearchCriterion对象。CombineTwoCriteria类的isSatisfied()方法同时检查这两个SearchCriterion对象。只有当这两个对象都返回true时,CombineTwoCriteria类的isSatisfied()方法才返回true。否则返回false。
public class CombineTwoCriteria implements SearchCriterion {
public CombineTwoCriteria(SearchCriterion a, SearchCriterion b) {
...
}
}
在SearchCriterionFactory类中新增一个公有的静态方法,命名为newCombineTwoCriteria()。该方法接收两个SearchCriterion对象,返回一个新创建的CombineTwoCriteria对象。
public static CombineTwoCriteria newCombineTwoCriteria(SearchCriterion a, SearchCriterion b);
在实现了逻辑"与"之后,我们还需要一个处理逻辑"或"的SearchCriterion对象。同我们实现逻辑"与"一样,我们可以使用类似的方法实现逻辑"或"(创建一个EitherOfTwoCriteria类)。但是,我们还可以使用另一种方法实现。
首先,我们在接口SearchCriterion上使用标注@FunctionalInterface。然后,在SearchCriterionFactory类中新增一个公有静态方法,命名为newEitherOfTwoCriteria()。类似的,该方法接收两个SearchCriterion对象,并返回一个新的SearchCriterion对象。
这里可以使用Lambda表达式,让Java编译器为我们创建一个实现了SearchCriterion接口的匿名类。示例代码如下所示。
public static SearchCriterion newEitherOfTwoCriteria(SearchCriterion a, SearchCriterion b) {
return (Course course) -> a.isSatisfied(course) || b.isSatisfied(course);
}
最后,我们再来整理一下StudentCenter类中提供的成员方法。我们在StudentCenter类中新增一个成员方法,其原型如下。
我们"借用"方法findCourse(CourseSearch search)来实现这个新方法,即在新方法中,实时的创建一个CourseSearch对象,将新方法的入参SearchCriterion"适配"到CourseSearch对象上。
public Iterator<Course> findCourse(SearchCriterion criterion){
return findCourse(CourseSearch.builder().criterion(criterion::isSatisfied).build());
}