Java8 Practical Combat - Behavior Parameterized Pass Code

background:

Let’s briefly summarize Chapter 2 based on "Java8 Practical Combat".

In software engineering, one of the most important issues is that user needs will continue to change. How to deal with changing needs and minimize the workload need to be considered, and behavioral parameterization is a software that handles frequent changing requirements. development mode.

In my opinion, behavior parameterization means taking out a piece of code and preparing it but not executing it. This code block will be called by other parts, which means that the execution of this code can be postponed.

The following uses the case of screening apples in inventory to illustrate.

Version 1: Screening Green Apples

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

The above code can be used to filter green apples, but if the demand changes and red apples need to be filtered, the first reaction is to add color conditions to the input parameters of the function above to match.

Version 2: Color as parameter

public static List<Apple> filterAppleByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<>();
    for(Apple app: inventory) {
            if(apple.getColor().equals(color)) {
                result.add(apple);
            }
    }
    return result;
}
List<Apple> greenApples = filterAppleByColor(Inventory, "green");
List<Apple> greenApples = filterAppleByColor(Inventory, "red");

This can get the final answer, but another problem arises, and now the demand becomes the need to be able to distinguish light apples from heavy apples.

So I copied the above code and wrote another one for weight.

Third Edition: Weight as Parameter

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

Although this is possible, it breaks the DRY (Don't Repeat Yourself, Don't Repeat Yourself) software engineering principle. Of course, if there is no repetition, you can also add a flag to determine which condition is being queried, as follows

Edition 4: Unified Query Functions for Color and Weight

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

The readability of such code is very poor, and the flags are now only color and weight. If you add query conditions, such as origin, shape, etc., it will not be able to meet the needs.

Behavior parameterized passing code

Consider modeling the selected standard, such as whether it is green or weighs more than 150 grams, to return a boolean value. This function that returns a boolean value is called a "predicate". Define an interface to select standard modeling:

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

Then you can have many implementation classes, such as

public class AppleHeavyWeightPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}

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

Encapsulate the different behaviors of different filter methods, called "strategies", and then select an algorithm at runtime. This algorithm family is applePredicate.

Fifth Edition: after applePredicate transformation

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

This is much more flexible to use. Suppose the selection requirements change and the combination conditions require both red apples and a weight of more than 150 grams. Then just add another class to implement ApplePredicate.

public class AppleRedAndHeavyPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor()) && apple.getWeight() > 150;
    }
}

List<Apple> redAndHeavyApple = filterApples(inventory, new AppleRedAndHeavyPredicate());

 But this is not perfect, because now filterApples input parameters need a new object related to the filter conditions, and implement the test method. In order to further reduce the code, you can also use anonymous classes and lambda expressions

Edition Six: Anonymous Classes

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

Anonymous classes seem to reduce the implementation code of the class, but there is still a lot of code stuffed in when calling. Then it’s the last one

Seventh Edition: Lambda Expressions

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

It looks much cleaner this way. Now filterApples is only used for filtering apples. We even go one step further and create a general filter that uses generics to replace Apple, as follows:

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

This way you can use the filter method on bananas, oranges, etc. The Predicate<T> here is a functional interface. In order to shorten the length, I will talk about it in the next blog.

Guess you like

Origin blog.csdn.net/u012895183/article/details/135027382