一、 行为参数化
通过将一个行为写成参数,再传进具体的执行代码中
举例来说: 你室友知道你要从校外回宿舍,他想让你帮他买写东西。这些东西可能是水,饭或者零食,水果,书等等。
这时你就有一个购买的行为。
这个购买的行为变成一个你回家过程中的一个参数,进行传递。那么,不过之后你室友怎么改变买的东西。你需要改变的只要购买这个行为,其他都可以不变。
二、主要作用:
为了应付需求的不断更改。
三、具体例子
例如:你给一个果农做了一个功能。这天,果农说他想筛选果子是苹果的数据。你这样写
public static List<Fruit> filterAppleFruit(List<Fruit> inventory){
List<Fruit> result = new ArrayList<>();
for (Fruit fruit:inventory) {
// 筛选出是苹果的水果
if ("apple".equals(fruit.getType())){
result.add(fruit);
}
}
return result;
}
又隔了一天,果农说:我想看看所有是香蕉的,行吗? 我:行!这时你咔咔改了下代码,变成这样
/**
* 筛选水果
* @param inventory 原水果数据
* @param type 水果(香蕉 苹果...)
* @return
*/
public static List<Fruit> filterTypeFruit(List<Fruit> inventory,String type){
List<Fruit> result = new ArrayList<>();
for (Fruit fruit:inventory) {
// 筛选出是苹果的水果
if (type.equals(fruit.getType())){
result.add(fruit);
}
}
return result;
}
使用时:
List<Fruit> appFruit = filterTypeFruit(inventory, "apple");
List<Fruit> bananaFruit = filterTypeFruit(inventory, "banana");
写完了,你感觉很棒,这回再改也不怕了。信心满满。结果第二天,果农说:这些水果要是能区分大小就好了,我想看看大于150克的数量。 我:...
没办法,继续吧~ 这时你就改了改,成功写完了。如下
/**
* 仅筛选水果重量
*
* @param inventory 原水果数据
* @param weight 水果重量
* @return
*/
public static List<Fruit> filterWeightFruit(List<Fruit> inventory, int weight) {
List<Fruit> result = new ArrayList<>();
for (Fruit fruit : inventory) {
// 筛选出是苹果的水果
if (fruit.getWeight() > weight) {
result.add(fruit);
}
}
return result;
}
写完后,嗯... 看起来不错。需求都已经实现了。第二天,果农又来了,说:我想看看重量大于150克的苹果有多少,行吗?
我:儒雅随和.微笑。
没事,继续写吧~ 于是你写出了这样的代码
public static List<Fruit> filterFruits(List<Fruit> inventory, String color,
int weight) {
List<Fruit> result = new ArrayList<Fruit>();
for (Fruit fruit : inventory) {
if (fruit.getColor().equals(color) && fruit.getWeight() > weight) {
result.add(fruit);
}
}
return result;
}
使用:
List<Fruit> fruits = filterFruits(inventory, "banana",150);
这样,一个需求又完成了。但是作为一个有追求的程序员,上面的方法还是令人失望的,他打破了DRY(Don’t Repeat Yourself,不要重复自己)的软件工程原则。这样之后如果需要筛选颜色,日期等等需求时,都需要重复的去写一个方法。这也太槽糕了。
那么,如果使用行为参数化来实现呢?
上述的方法其实都是在进行筛选这个行为,筛选后返回true或者false的boolean值。那么我们可以先定义一个接口,来对应这个筛选的行为。接口如下:
public interface FruitPredicate{
boolean test (Fruit apple);
}
那么现在你就可以通过FruitPredicate来实现不同的筛选标准了,如:
// 筛选重量大于150的
public class FruitHeavyWeightPredicate implements FruitPredicate{
@Override
public boolean test(Fruit fruit){
return fruit.getWeight() > 150;
}
}
// 筛选苹果的
public class FruitAppleTypePredicate implements FruitPredicate{
@Override
public boolean test(Fruit fruit){
return "apple".equals(fruit.getType());
}
}
上面是筛选的条件,筛选方法如下:
public static List<Fruit> filterFruits(List<Fruit> inventory,
FruitPredicate p){
List<Fruit> result = new ArrayList<>();
for(Fruit fruit: inventory){
if(p.test(fruit)){
result.add(fruit);
}
}
return result;
}
传入了筛选条件的抽象类,使用如下:
List<Fruit> redAndHeavyApples = filterFruits(inventory, new FruitAppleTypePredicate());
这个时候,已经很棒了~ filterFruits的方法取决于你通过FruitsPredicate对象传递的代码。换句话说,你把filterFruits的方法行为参数化了。
上述方法中,我们每次只需要更改或添加实现FruitsPredicate对象的类就可以了。从而达到多种行为(筛选行为)一个参数。从而达到你只使用一个方法,给它添加不同的行为来达到不同的目的或功能。
这时候可能有人又会觉得,这样每次实现一个需求,每次都得实现FruitsPredicate对象,然后再new再传入参数太麻烦,这时,你也可以使用匿名类。
如:
List<Fruit> appleApples = filterFruits(inventory, new FruitPredicate() {
@Override
public boolean test(Fruit fruit) {
return "apple".equals(fruit.getType());
}
});
或者使用 Lambda 表达式:
List<Fruit> appleApples = filterFruits(inventory, (FruitPredicate) fruit -> "apple".equals(fruit.getType()));
再将List类型抽象化:
public interface Predicate<T> {
boolean test (T t);
}
public static List<T> filterFruits(List<T> inventory,
Predicate<T> p){
List<T> result = new ArrayList<T>();
for(T t: inventory){
if(p.test(t)){
result.add(t);
}
}
return result;
}
这时你就不仅仅局限于水果了,还可以是其他,如用户(User)
List<User> appleApples = filterFruits(inventory, (Predicate<User>) user -> "张三".equals(user.getName()));
在Java 8中,List自带了一个sort方法就是使用该方式实现的,有兴趣可以看看。
关于JAVA 8中的行为参数化到这里就差不多了。不知道你们悟了吗?以上所有的内容均出自JAVA 8实战这本书,有时间的小伙伴还是去看书比较好。
只有自己写出来的,才是真正你自己的。那怕它一团槽
----- 宝哥