美文网首页
策略模式与行为参数化

策略模式与行为参数化

作者: tingshuo123 | 来源:发表于2018-09-29 23:30 被阅读25次

考虑这样一个场景,有一堆苹果,我们需要把绿色的苹果给筛选出来。

public class Apple {
    
    private String color;
    private int weight;
    // getter setter...
}

很简单的一个需求,我们可以马上写出下面这样这样的代码

public class FilterApple {
    
    
    // 筛选绿色的苹果
    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;
    }
}

像上面那样写的话看起来没有问题,但是仔细想想,需求是一直会发生变化如果需要筛选红色的苹果,怎么办?

如果向选择多种颜色的苹果呢(红色,绿色,浅绿色等)?

当然新建一个方法,复制上面的方法体后更改if的匹配条件可以解决,这样代码复用率会很低,而且不优雅。一个良好的原则是在编写类似的代码后,就尝试将其抽象化。

一种做法是给方法添加一个颜色参数(或者颜色的列表),这样就可以灵活的适应变化了.

改进后的代码:

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

又来了一个新需求,需要把大的苹果挑选出来(大于150克),有了上次的经验,这次我们肯定会想到添加一个 重量参数 来适应变化。

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

通过上面代码是可以解决现有的需求,但是我们会发现上面筛选重量跟筛选颜色两个方法相似度很高。
这显然违反了 DRY(Don't Reperat Yourself)的软件工程原则。
还有如果你想改变遍历的方式提升性能,那就需要改变所有的实现方法,这样不仅麻烦,也违反了开闭原则

违反开闭原则问题我们待会再解决,我们先把代码冗余问题先解决了,我们想办法把他给写成一个方法,接收两个筛选条件参数,跟一个标志

于是有了下面代码:

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

嗯,代码冗余的问题是解决了,但是这样的解决方法再差不过了
比如 true 和 false 是什么意思?
如果需求又改变要筛选其他属性怎么办?比如 品种、产地
或者多种属性组合,比如重的绿色的苹果你又该怎么办?
像前面的思路写的话的,无疑会有大量的重复filter方法,或者一个非常复杂的方法,参数列表会非常长。

想起来前几天才学了策略模式,我们可以用策略模式来很好的解决这个问题

首先我们先定义一个选择标准(策略)的接口

public interface ApplePredicate {
    /*
     * 你可以通过不同的实现类,封装不同的筛选算法(策略),然后再运行时选择一个筛选算法。
     * 这样有新需求时,你只需要再添加一个筛选规则实现ApplePredicate接口即可,不用改变原来代码
     */
    public boolean filter(Apple apple);
}

现在我们可以用 ApplePredicate 的不同实现,代表多个不同的选择标准(策略),如下

筛选绿色苹果:

public class AppleGreenColorPredicate implements ApplePredicate{
    
    @Override
    public boolean filter(Apple apple) {
        
        return "green".equals(apple.getColor());
    }

}

筛选重(大于150kg)的苹果:

public class AppleHeavyWeightPredicate implements ApplePredicate{

    @Override
    public boolean filter(Apple apple) {
        
        return apple.getWeight() > 150;
    }
}

接下来我们只需要让 filterApples方法接受ApplePredicate对象,让ApplePredicate对象对Apple` 做条件测试。

通过策略模式写的筛选函数:

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

这段代码比之前的代码要好多了,不通的筛选条件我们只需要创建不同的ApplePredicate对象可以了.

现在我们的代码可以应对任何涉及苹果筛选条件需求的变更了.

简单来说,行为参数化就是,让方法接受多种行为(策略),并再内部使用,以完成不通过的行为。

像上面的 filterApples方法的行为取决于通过 ApplePredicate 对象传递的代码,这样我们把 filterApples 的行为给参数化了。

现在 filterApples 方法的行为取决 ApplePredicate 对象传递的代码。


现在似乎看起来一切的完美了,想想上面的我们又是创建接口,又是实现接口的,但是我们需要的仅仅是一个方法,由于 filterApples 只接受对象,在JDK 1.8 之前这样做也是没办法的。

JDK1.8的出现该让我们能写出更加简洁的代码,JDK1.8推出的一个新特性 Lambda 表达式,通过 Lambda 表达式我们可以直接将 apple.getWeight() > 150 这样的表达式传递给 filterApple方法

用 Lambda 表达式重写 为下面的像样子

        List<Apple> inventory = new ArrayList<>();
        
        List<Apple> list = FilterApple.filterApples(inventory,
                (Apple apple) -> "red".equals(apple.getColor()));

这代码比之前的看起来要干净多了。


其实我们还可以更进一步的抽象,目前,filterApples方法还只适用于 Apple。 我们还可以将 List 类型抽象化,从而让它可以适用于更多的场景,如筛选梨子、橘子、西瓜。

public interface Predicate<T> {
    
    boolean test(T t);
}
public class FruitFilter {
    
    public static <T> List<T> filter(List<T> list, Predicate<T> p) {
        
        List<T> result = new ArrayList<>();
        
        for (T e : result) {
            if (p.test(e)) {
                result.add(e);
            }
        }
        
        return result;
    }
}
public class PredicateTest {
    
    // 筛选苹果
    @Test
    public void test() {
        
        List<Orange> inventory = new ArrayList<>();
        
        List<Orange> list = FruitFilter.filter(inventory,
                (Orange orange) -> "red".equals(orange.getColor()));
    }
    
    // 筛选橘子
    @Test
    public void test02() {
        
        List<Apple> inventory = new ArrayList<>();
        
        List<Apple> list = FruitFilter.filter(inventory,
                (Apple apple) -> "red".equals(apple.getColor()));
    }
}

相关文章

  • 策略模式与行为参数化

    考虑这样一个场景,有一堆苹果,我们需要把绿色的苹果给筛选出来。 很简单的一个需求,我们可以马上写出下面这样这样的代...

  • 策略模式与行为参数化

    策略模式 行为参数化 优化 自动选择最佳折扣 将所有折扣函数放在另一个文件中 添加一个自动获取最佳折扣的函数 通过...

  • Lambda表达式知识手册

    1.行为参数化 行为参数化是将方法的具体实现抽象化,java8之前可以使用接口(策略模式,根据不同需求需要编写...

  • 行为参数化------Java8 in action

    行为参数化是一种类似于策略设计模式的模式,可以轻松地适应不断变化的需求。 具体而言:行为参数化就是让一个方法接受多...

  • Java8

    List类型抽象化 关键字: 谓词 行为参数化 匿名类 Lambda 策略模式 Lambda表达式 函数式接口就是...

  • 《Java 8 实战》Ch2: 通过行为参数化传递代码

    李文轩 2019-04-20 行为参数化的目的是“应对不断变化的需求“;在需求发生改变时,用行为参数化策略可以保持...

  • java8——lambed表达式

    行为参数化的3中方式(策略模式) 类 匿名类 lamada 函数式接口 函数式接口就是只定义一个抽象方法的接口。 ...

  • Java Lambda表达式

    概念 行为参数化:让方法接受多种行为(策略)作为参数,并在内部使用,来完成不同的行为 缺失的方法主体随接口提供了(...

  • java 策略模式

    策略模式1.策略模式简介策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具...

  • 行为参数化/策略模式 && Lambda表达式

    这篇文章不涉及Lambda表达式的具体使用,只谈谈我对其的一些理解。不一定对,如有偏颇,望指正。 行为参数化/策略...

网友评论

      本文标题:策略模式与行为参数化

      本文链接:https://www.haomeiwen.com/subject/vncioftx.html