java1.8实战学习(三)——通过行为参数化传递代码

版权声明:转载请附上博客地址谢谢 https://blog.csdn.net/F_QWERDF/article/details/84834580

 上一篇:java1.8实战学习(二)——总结:流处理、行为参数化、并行与共享

 下一篇:java1.8实战学习(四)——通过行为参数化传递代码

  • 通过行为参数化传递代码

在软件工程中,一个众所周知的问题就是,不管你做什么,用户的需求肯定会变。比方说,有个应用程序是帮助农民了解自己的库存的。这位农民可能想有一个查找库存中所有绿色苹果的功能。但到了第二天,他可能会告诉你:“其实我还想找出所有重量超过150克的苹果。”又过了两天,农民又跑回来补充道:“要是我可以找出所有既是绿色,重量也超过150克的苹果,那就太棒了。”你要如何应对这样不断变化的需求?理想的状态下,应该把你的工作量降到最少。此外,类似的新功能实现起来还应该很简单,而且易于长期维护。

行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。一言以蔽之,它意味着拿出一个代码块,把它准备好却不去执行它。这个代码块以后可以被你程序的其他部分调用,这意味着你可以推迟这块代码的执行。例如,你可以将代码块作为参数传递给另一个方法,稍后再去执行它。这样,这个方法的行为就基于那块代码被参数化了。例如,如果你要处理一个集合,可能会写一个方法:

可以对列表中的每个元素做“某件事”

可以在列表处理完后做“另一件事”

遇到错误时可以做“另外一件事”

  • 应对不断变化的需求

初试牛刀:筛选绿苹果

第一个解决方案可能是这样:

public static List<Apple> filterGreenApples(List<Apple> inventory) { 
 List<Apple> result = new ArrayList<Apple>(); 
 for(Apple apple: inventory){ 
 if( "green".equals(apple.getColor() ) { //仅仅是选出绿苹果
 result.add(apple); 
 } 
 } 
 return result; 
} 

但是现在农民改主意了,他还想要筛选红苹果。你该怎么做呢?简单的解决办法就是复制这个方法,把名字改成filterRedApples,然后更改if条件来匹配红苹果。然而,要是农民想要筛选多种颜色:浅绿色、暗红色、黄色等,这种方法就应付不了了。一个良好的原则是在编写类似的代码之后,尝试将其抽象化。

再展身手:把颜色作为参数

一种做法是给方法加一个参数,把颜色变成参数,这样就能灵活地适应变化了:

public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) { 
 List<Apple> result = new ArrayList<Apple>(); 
 for (Apple apple: inventory){ 
 if ( apple.getColor().equals(color) ) { //根据参数color来选取苹果
 result.add(apple); 
 } 
 } 
 return result; 
} 

现在,只要像下面这样调用方法,农民朋友就会满意了: 

List<Apple> greenApples = filterApplesByColor(inventory, "green"); 
List<Apple> redApples = filterApplesByColor(inventory, "red"); 

 这位农民又跑回来和你说:“要是能区分轻的苹果和重的苹果就太好了。重的苹果一般是重量大于150克。”作为软件工程师,你早就想到农民可能会要改变重量,于是你写了下面的方法,用另一个参数来应对不同的重量:

public static List<Apple> filterApplesByWeight(List<Apple> inventory,  int weight) { 
 List<Apple> result = new ArrayList<Apple>(); 
 For (Apple apple: inventory){ 
 if ( apple.getWeight() > weight ){ 
 result.add(apple); 
 } 
 } 
 return result; 
} 

解决方案不错,但是请注意,你复制了大部分的代码来实现遍历库存,并对每个苹果应用筛选条件。这有点儿令人失望,因为它打破了DRY(Don’t Repeat Yourself,不要重复自己)的软件工程原则。如果你想要改变筛选遍历方式来提升性能呢?那就得修改所有方法的实现,而不是只改一个。从工程工作量的角度来看,这代价太大了。

你可以将颜色和重量结合为一个方法,称为filter。不过就算这样,你还是需要一种方式来区分想要筛选哪个属性。你可以加上一个标志来区分对颜色和重量的查询(但绝不要这样做!很快会解释为什么)。

第三次尝试:对你能想到的每个属性做筛选

一种把所有属性结合起来的笨拙尝试如下所示:

public static List<Apple> filterApples(List<Apple> inventory, String color,  int weight, boolean flag) { 
 List<Apple> result = new ArrayList<Apple>(); 
 for (Apple apple: inventory){ 
//十分笨拙的筛选方式
 if ( (flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight) ){
 result.add(apple); 
 } 
 } 
 return result; 
} 

你可以这么用(但真的很笨拙):

List<Apple> greenApples = filterApples(inventory, "green", 0, true); 
List<Apple> heavyApples = filterApples(inventory, "", 150, false); 

这个解决方案再差不过了。首先,客户端代码看上去糟透了。true和false是什么意思?此外,这个解决方案还是不能很好地应对变化的需求。如果这位农民要求你对苹果的不同属性做筛选,比如大小、形状、产地等,又怎么办?而且,如果农民要求你组合属性,做更复杂的查询,比如绿色的重苹果,又该怎么办?你会有好多个重复的filter方法,或一个巨大的非常复杂的方法。到目前为止,你已经给filterApples方法加上了值(比如String、Integer或boolean)的参数。这对于某些确定性问题可能还不错。但如今这种情况下,你需要一种更好的方式,来把苹果的选择标准告诉你的filterApples方法。

在下一节中,我们会介绍了如何利用行为参数化实现这种灵活性。

猜你喜欢

转载自blog.csdn.net/F_QWERDF/article/details/84834580
今日推荐