(文章转自CSDN)
2.Commons Collections中的算子
算子成为Commons Collections 3.1中的有趣的部分有两个原因:它们没有得到应得的重视并且它们有改变你编程的方式的潜力。算子只是一个奇特的名字,它代表了一个包装了函数的对象—一个“函数对象”。当然,它们不是一回事。如果你曾经使用过C和C++的方法指针,你就会理解算子的威力。
一个算子是一个对象—一个Predicate,一个Closure, 一个Transformer。
Predicates求对象的值并返回一个boolean,Transformer求对象的值并返回新对象,Closure接受对象并执行代码。算子可以被组合成组合算子来模仿循环,逻辑表达式,和控制结构,并且算子也可以被用来过滤和操作集合中的元素。在这么短的篇幅中解释清楚算子是不可能的,所以跳过介绍,我将会通过使用和不使用算子来解决同一问题(解释算子)。在这个例子中,从一个ArrayList中而来的Student对象会被排序到两个List中,如果他们符合某种标准的话。
成绩为A的学生会被加到honorRollStudents(光荣榜)中,得D和F的学生被加到problemStudents (问题学生)list中。学生分开以后,系统将会遍历每个list,给加入到光荣榜中学生一个奖励,并安排与问题学生的家长谈话的时间表。下面的代码不使用算子实现了这个过程:
List allStudents = getAllStudents();
// 创建两个ArrayList来存放荣誉学生和问题学生
List honorRollStudents = new ArrayList();
List problemStudents = new ArrayList();
// 遍历所有学生,将荣誉学生放入一个List,问题学生放入另一个
Iterator allStudentsIter = allStudents.iterator();
while( allStudentsIter.hasNext() ) {
Student s = (Student) allStudentsIter.next();
if( s.getGrade().equals( "A" ) ) {
honorRollStudents.add( s );
} else if( s.getGrade().equals( "B" ) &&
s.getAttendance() == PERFECT) {
honorRollStudents.add( s );
} else if( s.getGrade().equals( "D" ) ||
s.getGrade().equals( "F" ) ) {
problemStudents.add( s );
} else if( s.getStatus() == SUSPENDED ) {
problemStudents.add( s );
}
}
// 对于的有荣誉学生,增加一个奖励并存储到数据库中
Iterator honorRollIter =
honorRollStudents.iterator();
while( honorRollIter.hasNext() ) {
Student s = (Student) honorRollIter.next();
// 给学生记录增加一个奖励
s.addAward( "honor roll", 2005 );
Database.saveStudent( s );
}
// 对所有问题学生,增加一个注释并存储到数据库中
Iterator problemIter = problemStudents.iterator();
while( problemIter.hasNext() ) {
Student s = (Student) problemIter.next();
// 将学生标记为需特殊注意
s.addNote( "talk to student", 2005 );
s.addNote( "meeting with parents", 2005 );
Database.saveStudent( s );
}
上述例子是非常过程化的;要想知道Student对象发生了什么事必须遍历每一行代码。例子的第一部分是基于成绩和考勤对Student对象进行逻辑判断。
第二部分对Student对象进行操作并存储到数据库中。像上述这个有着50行代码程序也是大多程序所开始的—可管理的过程化的复杂性。但是当需求变化时,问题出现了。一旦判断逻辑改变,你就需要在第一部分中增加更多的逻辑表达式。
举例来说,如果一个有着成绩B和良好出勤记录,但有五次以上的留堂记录的学生被判定为问题学生,那么你的逻辑表达式将会如何处理?或者对于第二部分中,只有在上一年度不是问题学生的学生才能进入光荣榜的话,如何处理?当例外和需求开始改变进而影响到过程代码时,可管理的复杂性就会变成不可维护的面条式的代码。
从上面的例子中回来,考虑一下那段代码到底在做什么。它在一个List遍历每一个对象,检查标准,如果适用该标准,对此对象进行某些操作。上述例子可以进行改进的关键一处在于从代码中将标准与动作解藕开来。下面的两处代码引用以一种非常不同的方法解决了上述的问题。首先,荣誉榜和问题学生的标准被两个Predicate对象模型化了,并且加之于荣誉学生和问题学生上的动作也被两个Closure对象模型化了。这四个对象如下定义:
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;
// 匿名的Predicate决定一个学生是否加入荣誉榜
Predicate isHonorRoll = new Predicate() {
public boolean evaluate(Object object) {
Student s = (Student) object;
return( ( s.getGrade().equals( "A" ) ) ||
( s.getGrade().equals( "B" ) &&
s.getAttendance() == PERFECT ) );
}
};
//匿名的Predicate决定一个学生是否是问题学生
Predicate isProblem = new Predicate() {
public boolean evaluate(Object object) {
Student s = (Student) object;
return ( ( s.getGrade().equals( "D" ) ||
s.getGrade().equals( "F" ) ) ||
s.getStatus() == SUSPENDED );
}
};
//匿名的Closure将一个学生加入荣誉榜
Closure addToHonorRoll = new Closure() {
public void execute(Object object) {
Student s = (Student) object;
// 对学生增加一个荣誉记录
s.addAward( "honor roll", 2005 );
Database.saveStudent( s );
}
};
// 匿名的Closure将学生标记为需特殊注意
Closure flagForAttention = new Closure() {
public void execute(Object object) {
Student s = (Student) object;
// 标记学生为需特殊注意
s.addNote( "talk to student", 2005 );
s.addNote( "meeting with parents", 2005 );
Database.saveStudent( s );
}
};
这四个匿名的Predicate和Closure是从作为一个整体互相分离的。flagForAttention(标记为注意)并不知道什么是确定一个问题学生的标准 。现在需要的是将正确的Predicate和正确的Closure结合起来的方法,这将在下面的例子中展示:
import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;
Map predicateMap = new HashMap();
predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );
Closure processStudents =
ClosureUtils.switchClosure( predicateMap );
CollectionUtils.forAllDo( allStudents, processStudents );
在上面的代码中,predicateMap将Predicate与Closure进行了配对;如果一个学生满足作为键值的Predicate的条件,那么它将把它的值传到作为Map的值的Closure中。通过提供一个NOPClosure值和null键对,我们将把不符合任何Predicate条件的Student对象传给由ClosureUtils调用创建的“不做任何事”或者“无操作”的NOPClosure。
一个SwitchClosure, processStudents,从predicateMap中创建。并且通过使用CollectionUtils.forAllDo()方法,将processStudents Closure应用到allStudents中的每一个Student对象上。这是非常不一样的处理方法;记住,你并没有遍历任何队列。而是通过设置规则和因果关系,以及CollectionUtils和SwitchClosur来完成了这些操作。
当你将使用Predicate的标准与使用Closure的动作将分离开来时,你的代码的过程式处理就少了,而且更容易测试了。isHonorRoll Predicate能够与addToHonorRoll Closure分离开来进行独立的单元测试,它们也可以合起来通过使用Student类的模仿对象进行测试。第二个例子也会演示CollectionUtils.forAllDo(),它将一个Closure应用到了一个Collection的每一个元素中。
你也许注意到了使用算子并没用减少代码行数,实际上,使用算子还增加了代码量。但是,通过算子,你得到了将到了标准与动作的模块性与封装性的好处。如果你的代码题已经接近于几百行,那么请考虑一下更少过程化处理,更多面向对象的解决方案—通过使用算子。
Jakarta Commons Cookbook中的第四章“算子”介绍了Commons Collections中可用的算子,在第五章,“集合”中,向你展示了如何使用算子来操作Java 集合类API。
所有的算子-- Closure, Predicate, 和 Transformer—能够被合并为合并算子来处理任何种类的逻辑问题。switch, while和for结构能够被SwitchClosure, WhileClosure, 和 ForClosure模型化。
复合的逻辑表达式可以被多个Predicate构建,通过使用OrPredicate, AndPredicate, AllPredicate, 和 NonePredicate将它们相互联接。Commons BeanUtils也包含了算子的实现被用来将算子应用到bean的属性中-- BeanPredicate, BeanComparator, 和 BeanPropertyValueChangeClosure。算子是考虑底层的应用架构的不一样的方法,它们可以很好地改造你编码实现的方法。
分享到:
相关推荐
《Jakarta Commons Cookbook》是Java开发领域中一本非常实用的指南,主要涵盖了Apache Jakarta Commons组件的使用技巧和最佳实践。这本书旨在帮助开发者更好地理解和利用Jakarta Commons库中的各种工具类和模块,...
其中,Lang组件是Jakarta Commons中的一个核心模块,它提供了大量的静态方法,用于处理基本Java对象,如字符串、数组、日期等。本文将深入探讨Jakarta Commons Lang组件以及在`commons-lang-2.3-src.zip`压缩包中的...
Apache Jakarta Commons项目汇集了一系列高质量且可重用的Java库,它们广泛应用于企业级应用开发之中。 #### 二、主要组件介绍 ##### 2.1 FileUpload - **功能简介**:`FileUpload` 是一个用于解析HTTP请求中的多...
jakarta-commons 相关依赖包,文件列表: commons-attributes-api.jar commons-attributes-compiler.jar commons-beanutils.jar commons-codec.jar commons-collections.jar commons-dbcp.jar commons-digester.jar ...
jakarta commons-logging 1.1.1
《Jakarta Commons Cookbook》是Java开发者的一份宝贵资源,它包含了一系列实用的工具类和组件,可以帮助开发者在日常编程工作中提高效率。Jakarta Commons是Apache软件基金会的一个项目,旨在为Java社区提供一系列...
在Java开发中,Jakarta Commons库通常被用来提升代码质量,减少重复工作,并提供了一套强大且灵活的工具集。通过深入学习这些API,开发者可以更有效地利用这些组件,提高开发效率和代码的可维护性。同时,Jakarta ...
《Jakarta Commons Cookbook》是Java开发者的一本重要参考资料,它主要涵盖了Apache Jakarta Commons项目中的各种组件和工具的使用方法。Apache Commons是Java开发中的一个关键部分,提供了许多实用且功能强大的类库...
除了上述组件外,Jakarta Commons 还包括 BeanUtils、Codec、 Digester 等,它们提供了诸如对象属性映射、编码解码、XML 解析等功能,大大简化了 Java 开发中的常见任务。 通过了解和使用 Jakarta Commons,开发者...
本书是一本介绍apache commons使用的电子书
这些组件都是 Jakarta Commons 中的一部分,它们各自解决了特定问题,为 Java 开发人员提供了强大的工具,提高了开发效率和代码质量。在实际开发中,可以根据需求选择合适组件进行集成,从而快速实现功能,降低开发...
Jakarta Commons-lang API & Source Code Jakarta lang包API帮助文档,并带有源码
Jakarta Commons 也包含了缓存解决方案,如Ehcache,可以帮助开发者在应用中实现高效的缓存策略,提高系统性能。 9. **Email**: Email 模块简化了发送电子邮件的过程,提供了构建和发送邮件的接口,支持HTML邮件...
Jakarta Commons IO是Java开发中的一个关键库,它由Apache软件基金会维护,为处理输入/输出操作提供了大量的实用工具和类。这个库弥补了Java标准库在IO操作上的不足,使得开发者可以更方便地处理文件、流、字符编码...
Jakarta Commons是一系列开源Java组件的集合,最初...Jakarta Commons的使用非常广泛,无论是在小型项目还是企业级应用中,都能找到它的身影。它的可重用性和成熟的文档使***a Commons成为了Java社区中一个宝贵的资源。
### Jakarta Commons FileUpload 用户指南知识点总结 #### 一、FileUpload 概览 - **Jakarta Commons FileUpload** 是一个用于处理HTTP文件上传的Java库,它基于**RFC1867**标准来解析和处理文件上传请求。 - **...
Jakarta commons docs API CHM 格式带索引和全文搜索,方便携带和查询。 Jakarta commons 包含很多可复用的通用组件。 commons-attributes 让开发者可以使用 C# 或 .net 样式的 attributes, 是一种运行时的 api, ...
《Jakarta Commons在线书架》一书深入探讨了Jakarta Commons框架的核心概念与应用实践,为读者提供了一次全面理解并掌握Jakarta Commons组件的机会。本书由Vikram Goyal编写,版权归属Manning Publications,出版于...