在软件工程中,一个众所周知的问题就是,无论作什么,用户的需求确定会变。
如何应对这样不断变化的需求?理想的状态下,应该把的工做量降到最少。此外,相似的新功能实现起来还应该很简单,并且易于长期维护。
行为参数化就是能够帮助处理频繁变动的需求的一种软件开发模式。一言以蔽之,它意味着拿出一个代码块,把它准备好却不去执行它。这个代码块之后能够被程序的其余部分调用,这意味着能够推迟这块代码的执行。
以筛选苹果为例,逐步改进代码,来展现一些让代码更灵活的最佳作法。
需求:筛选绿色苹果java
List<Apple> inventory = Arrays.asList(new Apple(80, "green"), new Apple(155, "green"), new Apple(120, "red")); public class Apple { private int weight; private String color;
// get setter ...
}
List<Apple> apples = filterGreenApples(inventory); public static List<Apple> filterGreenApples(List<Apple> inventory) { List<Apple> result = new ArrayList<>();//累积苹果的列表 for (Apple apple : inventory) { if ("green".equals(apple.getColor())) {//仅仅选出绿苹果 result.add(apple); } } return result; }
需求变化:筛选其余颜色的苹果算法
List<Apple> apples = filterApplesByColor(inventory,"red"); public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (apple.getColor().equals(color)) { result.add(apple); } } return result; }
需求变化:刷选颜色加剧量设计模式
public static List<Apple> filterApples(List<Apple> inventory, String color,int weight) { //...
这个解决方案仍是不能很好地应对变化的需求。app
List<Apple> apples = filterApples(inventory, new AppleColorPredicate()); 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; } interface ApplePredicate { public boolean test(Apple a); } static class AppleWeightPredicate implements ApplePredicate { public boolean test(Apple apple) { return apple.getWeight() > 150; } } static class AppleColorPredicate implements ApplePredicate { public boolean test(Apple apple) { return "green".equals(apple.getColor()); } }
附:刚作的这些和“策略设计模式”相关,它让定义一族算法,把它们封装起来(称为“策略”),而后在运行时选择一个算法。在这里,算法族就是ApplePredicate,不一样的策略就是AppleHeavyWeightPredicate和AppleGreenColorPredicate。 可是,该怎么利用ApplePredicate的不一样实现呢?须要filterApples方法接受ApplePredicate对象,对Apple作条件测试。这就是行为参数化:让方法接受多种行为(或战略)做为参数,并在内部使用,来完成不一样的行为。 要在咱们的例子中实现这一点,要给filterApples方法添加一个参数,让它接受ApplePredicate对象。这在软件工程上有很大好处:如今把filterApples方法迭代集合的逻辑与要应用到集合中每一个元素的行为区分开了。测试
请注意,在这个例子中,惟一重要的代码是test方法的实现;正是它定义了filterApples方法的新行为。因为该filterApples方法只能接受对象,因此必须把代码包裹在ApplePredicate对象里。的作法就相似于在内联“传递代码”,由于是经过一个实现了test方法的对象来传递布尔表达式的。spa
这种行为参数化的好处在于能够把迭代要筛选的集合的逻辑与对集合中每一个元素应用的行为区分开来。这样能够重复使用同一个方法,给它不一样的行为来达到不一样的目的设计
List<Apple> redApples = filterApples(inventory, new ApplePredicate() { // 直接内联参数化filterapples方法的行为 public boolean test(Apple apple) { return "red".equals(apple.getColor()); } });
附:匿名类和熟悉的Java局部类(块中定义的类)差很少,但匿名类没有名字。它容许同时声明并实例化一个类。换句话说,它容许随用随建。code
List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
在通往抽象的路上,咱们还能够更进一步。目前,filterApples方法还只适用于Apple。还能够将List类型抽象化,从而超越眼前要处理的问题:对象
public interface Predicate<T> { boolean test(T t); } public static <T> List<T> filter(List<T> list, Predicate<T> p) { //引入类型参数T List<T> result = new ArrayList<>(); for (T e : list) { if (p.test(e)) { result.add(e); } } return result; }
List<Apple> redApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor())); System.out.println(redApples); List<Integer> numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0); System.out.println(evenNumbers);
参考:java8实战第二章blog