二、行为参数化

2018-01-22  本文已影响0人  小Noodles

如何应对不断变化的需求

软件工程中,用户的需求肯定会变,如何应对不断变化的需求,理想状态下,应该把工作量降到最少,此外,类似的新功能实现还应该很简单,而且易于长期维护。
行为参数话就是可以帮助你处理频繁变更需求的一种软件开发模式:拿出一个代码块,准备好不去执行它,这个代码块以后可以被程序的其它部分调用,意味着可以推迟这块代码的执行,例如,可以将代码块作为参数传递给另一个方法,稍后再去执行它。

public static List<Apple> filterGreenApples(List<Apple> appleList){
        List<Apple> result = new ArrayList<Apple>();
        for (Apple apple : appleList){
            if("green".equals(apple.getColor())){
                result.add(apple);
            }
        }
        return result;
    }

农民改主意了,还想要筛选红苹果,该怎么做?简单的解决办法就是复制这个方法,把名字改成filterRedApples,更改if匹配条件,然而,农民想要筛选多种颜色:浅绿色,暗红色,黄色等,复制的方法应付不了。

2.把颜色作为参数, 灵活适应变化

public static List<Apple> filterApplesByColor(List<Apple> appleList, String color){
        List<Apple> result = new ArrayList<>();
        for(Apple apple: appleList){
            if(apple.getColor().equals(color)){
                result.add(apple);
            }
        }
        return result;
    }

太简单了!
农民伯伯又跑来对你说,要是能区分,轻的苹果和重的苹果就太好了。

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

不错的解决方案,但是,复制了大部分的代码来实现遍历库存,对每个苹果应用筛选条件,打破了DRY的软件开发原则,如果有要改变筛选的遍历方式来提升性能呢?那就得修改所有方法的实现.

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

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

4.行为参数化

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

现在可以用ApplePredicate的多个实现代表不同的选择标准

//选择重的
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());
        }
    }
选择苹果的不同策略.png
public static List<Apple> filterApple(List<Apple> inventory, ApplePredicate p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(p.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }

5.匿名类

List<Apple> redApples2 = filterApple(inventory, new ApplePredicate() {
            public boolean test(Apple a){
                return a.getColor().equals("red"); 
            }
        });

6.使用Lambda表达式

List<Apple> redApples4 = filter(inventory, 
  (Apple apple) -> "red".equals(apple.getColor()));
行为参数化.png

7.将List类型抽象化

//引入泛型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;
    }
//例子 排序
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
//线程执行代码块
Thread t = new Thread(() -> System.out.println("Hello"));
//GUI事件处理
button.setOnAction((ActionEvent event) ->label.setText("Sent!"));

Lambda 表达式

Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(stringList, nonEmptyStringPredicate);

2.Consumer
java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void),你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口

forEach(Arrays.asList(1,3,4,5), (Integer i) -> System.out.println(i));

3.Function
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)

List<Integer> l = map(Arrays.asList("lambda", "Eric", "stream"), (String s) -> s.length());

4.原始类型特性化
Java类型要么是引用类型(比如Byte、 Integer、 Object、 List) ,要么是原始类型(比如int、 double、 byte、 char)。但是泛型(比如Consumer<T>中的T)只能绑定到引用类型。
IntPredicate 等等等 避免使用Lambda时自动装箱,拆箱操作

同一个Lambda可用于多个不同的函数式接口

Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
如何构建方法引用
1-Lambda-方法引用.png

构造函数引用
不将构造函数实例化却能够引用它,这个功能有一些有趣的应用

    static Map<String, Function<Integer, Fruit>> map = new HashMap<>();
    static{
        map.put("apple", Apple::new);
        map.put("orange", Orange::new);
    }

    public static Fruit getMeFruit(String fruit, Integer weight){
        return map.get(fruit).apply(weight);
    }
        Comparator<Apple> c = comparing(Apple::getWeight);
        /**逆序*/
        inventory.sort(comparing(Apple::getWeight).reversed());
        /**比较器链*/
        inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor));

2.谓词复合

        Predicate<Apple> redApple = a -> "red".equals(a.getColor());
        Predicate<Apple> notRedApple = redApple.negate();
        Predicate<Apple> heavyApple = a -> a.getWeight() > 100;
        Predicate<Apple> redAndHeavyApple = redApple.and(heavyApple);

3.函数复合

        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * 2;
        //g(f(x))
        Function<Integer, Integer> h = f.andThen(g);
        int result = h.apply(1);
        //f(g(x))
        Function<Integer, Integer> h1 = f.compose(g);
        int result2 = h1.apply(1);
上一篇 下一篇

猜你喜欢

热点阅读