日新月异的需求
作为程序员,有一个很常见的问题是,同样的东西,用户的需求会经常改变
像我们之前找苹果的例子,需求经常变在动,如果都实现,那不得累死我们程序员?
如何应对多变的需求
- 如果需求变成筛选红苹果,那我们需要拷贝这个方法,把Green换成Red。这样就违反了DRY
- 我们可以把颜色作为参数
- 仅仅改变颜色,把颜色传递进去,可以省去一个方法
此时又有新的需求:要区分重的苹果和轻的苹果:
行为参数化
- 经过上面的案例,我们需要一种更好的方式来应对变化的需求。
- 建模如下:你考虑的是苹果,需要根据Apple的某些属性,返回一个满足条件的boolean值。
- 我们称之为谓词,定义接口如下
可以使用ApplePredicate的多个实现来执行不同的行为
这些实现,就是不同的筛选条件,算法就是 ApplePredicate。怎么去利用这种不同的实现呢?需要filterApples方法接受ApplePredicate对象,对Apple做条件测试。那么现在就来修改之前的代码:
现在你把filterApples方法迭代集合的逻辑与你要应用到集合中每个元素的行为(这里是一个谓词)区分开了。
这样一来,任何需求的变更,只需要增加相应的实现类即可。filterApples方法的行为取决于你通过ApplePredicate对象传递的代码。但是会有一个问题:类膨胀。代码传递/行为在上述的实现中,我们发现,对于整个测试,唯一重要的就是test方法的实现,这个方法决定了需要怎么样进行过滤。所以在传递行为的时候,我们可以直接使用匿名内部类来传递,如下对于多个无用的代码,把匿名内部类更改为Lambda表达式就行
- 新需求,对苹果进行遍历,然后对其进行格式化输出
- 这个需求需要定义一个新的方法,prettyPrintApple,整体的执行框架如下
- ???就是我们要填充的部分:输入一个Apple,然后输出一个String,来定义一个接口 FormatApple
prettyPrintApple 就可以实现了,如下
- 到这里,我们可以将类、匿名类、Lambda进行行为参数化,替代了之前的案例中的值参数化。
- 现在我们发现上述的ApplePredicate只能处理Apple,且filterApples也只能处理Apple,所以我们将这部分使用泛型进行抽象化,如下
- 其他的行为参数化案例
- 对集合进行排序,根据苹果颜色排序或者根据大小排序
- 在Java 8中,List自带了一个sort方法(也可选择Collections.sort)排序
- sort的行为可以用java.util.Comparator对象来参数化,它的接口如下
你可以随时创建Comparator的实现,用sort方法表现出不同的行为。比如,你可以使用匿名类,按照重量升序对库存排序
Lambda表达式
小结
- 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
- 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。
- 传递代码,就是将新行为作为参数传递给方法。但在Java 8之前这实现起来很啰嗦,在Java 8之前可以用匿名类来减少。
- Java API包含很多可以用不同行为进行参数化的方法,包括排序、线程和GUI处理。