第一篇 通过行为参数化应对不断变化的需求

1.应对不断变化的需求

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

下面我们使用一个场景去介绍行为参数化的概念

1.1.筛选绿苹果

	public static List<Apple> filterGreenApples(List<Apple> inventtory) {
		List<Apple> result = new ArrayList<>(); //累积的苹果列表
		for (Apple apple : inventtory) {
		    //这里就是筛选绿苹果所需的条件
			if ("green".equals(apple.getColor())) { //仅仅选出绿苹果
				result.add(apple);
			}
		}
		return result;
	}

假如我们想筛选出红色的苹果,简单的解决办法就是复制这个方法,把名字改为filterRedApples,然后更改if条件来匹配苹果。然而后面可能会有更多的筛选要求,一个良好的原则就是编写类似的代码之后,尝试将其抽象化。

1.2.把颜色作为参数

把颜色作为参数传递给方法,这样就能灵活的适应变化了:

	public static List<Apple> filterApplesByColor(List<Apple> inventtory, String color) {
		List<Apple> result = new ArrayList<>();
		for (Apple apple : inventtory) {
		     //这里就可以将颜色传递过来,筛选所需颜色的苹果
			if (apple.getColor().equals(color)) {
				result.add(apple);
			}
		}
		return result;
	}

这样我们就能很方便的筛选出不同颜色的苹果了,但是如果我们要区分苹果的重量呢?也许你可能早就想到了,然后给出下面的代码:

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

这个方案确实不错,但是请注意,你却复制了大部分的代码来实现遍历库存,并对每个苹果应用筛选条件。如果现在你想改变筛选遍历方式来提升性能,这个时候你不得不修改所有方法的实现了。你可以将颜色和重量结合为一个方法,称为filter,不过即使这样,你还是需要一种方式来区分想要筛选哪个属性。你可以加一个标识来区分对颜色和重量的查询。

1.3.对每一种能想到的属性做筛选

	public static List<Apple> filterApples(List<Apple> inventtory, String color, int weight, boolean flag) {
		List<Apple> result = new ArrayList<>();
		for (Apple apple : inventtory) {
		    //十分笨拙的选择颜色或是重量的方式
			if ((flag && apple.getCollor().equals(color)) || (!flag && apple.getWeight() > weight)) {
				result.add(apple);
			}
		}
		return result;
	}

这样的代码是非常糟糕的,首先客户端代码看上去就很差,flag的true和false的表意并不是很明确。如果我们要使用不同的属性做筛选,而且使用不同的属性组合做筛选,又该怎么办呢?

2.行为参数化

从前面的介绍中你可以看到,你需要一种比添加很多参数更好的方法来应对变化的方法来应对变化的需求。让我们后退一步来看看更高层次的抽象。一种可能的解决方案是对你的选择标准建模:你考虑的是苹果,需要根据Apple的某些属性来返回一个Boolean值。让我们来定义一个接口来对选择标准建模:

	public interface ApplePredicate {
		boolean test(Apple apple);
	}

现在你可以使用不同的ApplePredicate 实现代表不同的选择标准了,比如:

	public class AppleHeavyWeightPredicate implements ApplePredicate {
        //筛选出重量大于40的苹果
		@Override
		public boolean test(Apple apple) {
			return apple.getWeight() > 40;
		}

	}
	public class AppleGreenColorPredicate implements ApplePredicate {
        //筛选出绿色苹果
		@Override
		public boolean test(Apple apple) {
			return "green".equals(apple.getCollor());
		}

	}

你可以把这些不同的实现看作是filter方法的不同行为。刚刚我们所作的和“策略模式”相关,它让你定义一族算法,把他们封装起来,然后再运行的时候选择一个算法。在这里算法族就是ApplePredicate,不同的策略就是AppleHeavyWeightPredicate和AppleGreenColorWeightPredicate。

需要怎样运用ApplePredicate这些不同实现呢?你需要filterApples方法接收一个ApplePredicate参数。这样做还有一个好处就是,你把迭代集合的逻辑和要应用到集合中每一个元素的行为区分开了。

2.1.根据抽象条件筛选

利用ApplePredicate改造后的filter方法类似下面的这样:

	public static List<Apple> filterApples(List<Apple> inventtory, ApplePredicate p) {
		List<Apple> result = new ArrayList<>();
		for (Apple apple : inventtory) {
			if (p.test(apple)) {
				result.add(apple);
			}
		}
		return result;
	}

现在我们就可以创建不同的ApplePredicate对象,并将它们传递给filterApples方法,代码就有足够的灵活性,可以应对任何涉及苹果属性的需求变更了:

	public class AppleRedAndHeavyPredicate implements ApplePredicate {

		@Override
		public boolean test(Apple apple) {
			return "red".equals(apple.getCollor()) && apple.getWeight() > 40;
		}
	}
	List<Apple> redAndHeavyApple = filterApples(inventtory,new AppleRedAndHeavyPredicate());

filterApples方法的行为取决于你通过ApplePredicate对象传递的代码,也就是说你把filterApples方法的行为参数化了。请注意,在上面的代码中唯一重要的代码就是test方法的实现,正是它定义了filterApples方法的新行为。但是由于filterApples方法只能接收对象,所以你必须把代码包裹在ApplePredicate对象里。

3.简化代码

为了把新的功能传递给filterApples方法,你不得不声明好几个实现了ApplePredicate接口的类,然后实例化好几个只会提到一次的ApplePredicate对象。

	public class AppleHeavyWeightPredicate implements ApplePredicate {

		@Override
		public boolean test(Apple apple) {
			return apple.getWeight() > 40;
		}

	}

	public class AppleGreenColorPredicate implements ApplePredicate {

		@Override
		public boolean test(Apple apple) {
			return "green".equals(apple.getCollor());
		}

	}
public class FilteringApples{

 public static void main(String[] args) {
	List<Apple> inventory = Arrays.asList(new Apple("green", 80), new Apple("green", 155), new Apple("red", 120));
	List<Apple> heavyApples = filterApples(inventory, new AppleHeavyWeightPredicate());
	List<Apple> greenApples = filterApples(inventory, new AppleGreenColorPredicate());
   }
 
	public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
		List<Apple> result = new ArrayList<>();
		for (Apple apple : inventory) {
			if (p.test(apple)) {
				result.add(apple);
			}
		}
		return result;
	}
}

3.1.使用匿名类

java有一种机制称为匿名类,可以让你同时声明和实例化一个类,这样就可以进一步改善代码。

	List<Apple> redApples =filterApples(inventory, new ApplePredicate() {
			
		@Override
		public boolean test(Apple apple) {
			return "red".equals(apple.getCollor());
		}
	});

但是匿名类还是有很多模板代码,会占用空间,而且有很多匿名类一起使用的时候会让代码很难理解。更好的解决办法就是使用Lambda表达式。

3.2.使用Lambda表达式

	@FunctionalInterface
	public interface ApplePredicate {
		boolean test(Apple apple);
	}

	List<Apple> result = filterApples(inventory,(Apple apple)->"red".equals(apple.getColor()));

3.3.将List类型抽象

目前我们的filterApples只试用于Apple方法,我们可以将List类型抽象化,从而超越眼前要处理的问题:

public interface Predicate<T>{
   boolean test(T t);
}
	public static <T> List<T> filter(List<T> list, Predicate<T> p) {
		List<T> result = new ArrayList<>();
		for (T e : list) {
			if (p.test(e)) {
				result.add(e);
			}
		}
		return result;
	}

现在你可以把filter应用到其他任意列表上了。现在我们就在灵活性和简洁性之间找到了最佳的平衡点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豢龙先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值