Java8 combat - passing code through behavior parameterization

1. First try: Screening green apples

A first solution might look like this:

 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;
    }

The above code only filters green apples. Now, I also want to filter out red apples. What should I do? The simple solution is to write a method repeatedly, and then change the condition to red apple. However, there are many kinds of colors to be filtered, which will make the code very redundant, so the first step is to try to abstract it.

2. Play again: use color as a parameter

public static List<Apple> filterGreenApples(List<Apple> inventory,String color){
        List<Apple> result=new ArrayList<>();
        //颜色作为参数
        for (Apple apple : inventory) {
            if (color.equals(apple.getColor())){
                result.add(apple);
            }
        }
        return result;
    }

But now I want to filter apples according to their weight, so is it necessary to use another parameter to express the weight of apples? Then I want to add a flag to distinguish the query of color and weight? The following is to show the general way of writing, but it is very silly.

3. Third attempt: filter for every attribute you can think of

   public static List<Apple> filterGreenApples(List<Apple> inventory,String color,int weight,boolean flag){
        List<Apple> result=new ArrayList<>();
        for (Apple apple : inventory) {
            if (flag && color.equals(apple.getColor())|| (!flag && apple.getWeight()>weight)){
                result.add(apple);
            }
        }
        return result;
    }

4. Willows and Flowers Bright: Behavioral Parameterization

We can parameterize behavior to achieve a higher level of abstraction, first define a unified standard interface, and then implement it through different subclasses, which is somewhat similar to the strategy design pattern.

//封装了对选择苹果的策略
public interface ApplePredicate {

    //具体算法交给子类去实现
    boolean test (Apple apple);
}
//颜色算法
public class AppleGreenColorPredicate implements  ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.getColor());
    }
}
//重量算法
public class AppleHeavyWeightPredicate implements  ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return apple.getWeight()>150;
    }
}

5. Fourth attempt: filter based on abstract conditions

   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;
    }

When we use it, we can pass different strategies to achieve our goals

List<Apple> heavyApples = filterApples(inventory, new AppleHeavyWeightPredicate());         
List<Apple> greenApples = filterApples(inventory, new AppleGreenColorPredicate());

But there is a problem with this, that is, I have to define an implementation class for each strategy to implement a certain algorithm. As a result, if there are many strategies later, a lot of classes will be added. We know that using anonymous classes is also a good choice.

6. Fifth Attempt: Using Anonymous Classes

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

But here comes the problem. Anonymous classes are not good enough. First, they tend to be bulky and take up a lot of space. Second, they are confusing to use, resulting in poor code readability. To a certain extent, the verbose problem of declaring several entity classes for an interface is improved, but it is still unsatisfactory. The lambda expression introduced since java8, a more concise way of passing code, solves this problem. Let's rewrite the previous code using lambda expressions.

7. Sixth Attempt: Using Lambda Expressions

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

I have to admit that using lambda expressions to rewrite the previous code is indeed a lot cleaner, because it looks more like the problem statement itself, solving the long-winded problem

8. The seventh attempt: abstracting the List type

On the road to abstraction, we can go further. At present, the filterApples method is only applicable to Apple, we can also try to apply it to other fruits.

public interface Predicate<T> {
    boolean test(T 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;
    }

Now you can apply the filter method to lists of oranges, bananas, etc.

9. Summary

Behavior parameterization is the ability of a method to receive different behaviors as parameters and use them internally to complete different behaviors.

Behavioral parameterization can make the code better adapt to changing requirements and reduce the workload in the future.

Passing code is passing the new behavior as a parameter to the method, but it was verbose to implement before java8. Long-winded code caused by declaring many entity classes that are only used once for an interface can be reduced by anonymous classes prior to Java 8.

The java API contains many methods that can be parameterized with different behaviors, including sorting, threading, etc.

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324189290&siteId=291194637