策略模式的妙用(java8中 lambda表达式的灵活使用)

x33g5p2x  于2021-12-18 转载在 其他  
字(4.9k)|赞(0)|评价(0)|浏览(467)

如果你还不熟悉java8的新特性,可以看下下面这篇文章 或者 B站红康师傅讲解的java8新特性,宋红康老师讲的不止有java8哦,乃至java 9-15的新特性都有,可以在B站中找到,或者尚硅谷官网可以免费获取到
 《B站尚硅谷官方运营号地址

想要了解四大类型(Consumer、Supplier、Predicate、Funtion)接口的看下前置文章:

java8新特性—大总结–建议收藏、点赞、加关注!!!

如果此前已经掌握了java8的新特性,至少lambda表达式是没啥大问题的。

请听题

要求设计一个计算器,其功能是完成两个数的四则运算(加减乘除)。

例如:有两个操作数 a 和 b 都是double类型的。

初级程序员的写法,具有一定的封装思想。

下面代码看起来没有问题,很nice 对吧。

public class Demo {

    public static void main(String[] args) {
        double a = 12, b = 3, result = 0;
        result = Calculator.run("+", a, b);
        System.out.println("加法 a + b = "+result); // 调用工具类得到结果
        result = Calculator.run("-", a, b);
        System.out.println("减法 a - b = "+result);
        result = Calculator.run("*", a, b);
        System.out.println("乘法 a * b = "+result);
        result = Calculator.run("/", a, b);
        System.out.println("除法 a / b = "+result);
    }
}

/** * 封装的一个工具类 */
class Calculator {
    /** * @param operatorString 运算符 * @param a 操作数1 * @param b 操作数2 * @return a,b经过运算符的结果值 */
    public static double run(String operatorString, double a, double b) {
        if (operatorString.equals("+")) {
            return a + b;
        } else if (operatorString.equals("-")) {
            return a - b;
        } else if (operatorString.equals("*")) {
            return a * b;
        } else if (operatorString.equals("/")) {
            return a / b;
        } else {
            throw new IllegalArgumentException("非法操作符:" + operatorString);
        }
    }
}

如果你有一些工作经验了,你会发现像这种if-else的写法很不好,看起来很不舒服。或许你会想到用switch进行代替,但依然还是不舒服,因为写在一起,很容易看错,稍微不注意你就会因为这个if-else埋坑。出现bug就很不好改

如果你之前看过我另外一篇文章,利用enmn代替if-else用枚举代替if-else的参考写法

有一定工作经验的程序员写法,利用枚举代替 if-else
import java.util.HashMap;
import java.util.Map;

public class EnmuMode {
    private final static Map<String, String> map;

    static {
        map = new HashMap<>();
        map.put("+", "ADD");
        map.put("-", "SUBTRACT");
        map.put("*", "MULTIPLY");
        map.put("/", "DIVIDION");
    }

    public static void main(String[] args) {
        double a = 12, b = 3, result = 0;
        String option= "*"; // 运算操作

        String strategy = map.getOrDefault(option, "EXCEPTION");// 得到策略字符串
        result = CalculateEnum .valueOf(strategy).calculate(leftNum, rightNum);// 根据策略调用得到对应枚举,调用对应的方法得到计算的结果

        System.out.println(result); // 打印计算后的结果
    }
}

enum CalculateEnum {

    ADD {
        @Override
        public double calculate(double leftNum, double rightNum) {
            return leftNum + rightNum;
        }
    },
    SUBTRACT {
        @Override
        public double calculate(double leftNum, double rightNum) {
            return leftNum - rightNum;
        }
    },
    MULTIPLY {
        @Override
        public double calculate(double leftNum, int rightNum) {
            return leftNum * rightNum;
        }
    },
    DIVIDION {
        @Override
        public double calculate(double leftNum, double rightNum) {
            return leftNum / rightNum;
        }
    },
    EXCEPTION {
        @Override
        public double calculate(double leftNum, double rightNum) {
            throw new IllegalArgumentException("运算操作不支持");
        }
    };

	/** * 将操作数 leftNum,rightNum 运算得到新值 * return 计算后的结果值 */
    double calculate(double leftNum, double rightNum);

}
而java8程序员的写法

定义一个函数式接口,使用lambda表达式完成功能

public class JAVA8Demo {

    public static void main(String[] args) {
        CalculateImpl calculate = new CalculateImpl(); // 得到计算器的实现类
        double a = 12, b = 3;
        double result = calculate.test((x, y) -> x + y, a, b);
        System.out.println("加法 a+b=" + result);
        double result2 = calculate.test((x, y) -> x - y, a, b);
        System.out.println("减法 a-b=" + result2);
        double result3 = calculate.test((x, y) -> x * y, a, b);
        System.out.println("乘法 a*b=" + result3);
        double result4 = calculate.test((x, y) -> x / y, a, b);
        System.out.println("除法 a/b=" + result4);

    }
}
/** * 封装接口类,用于进行java8的数据流传递 */
class CalculateImpl {
    public double test(Calculate calculate, double a, double b) {
        return calculate.run(a, b); // 将计算后的结果值直接返回
    }
}

/*定义一个函数式接口*/
interface Calculate {
	/** * 调用方法得到a,b的结果值 */
    double run(double a, double b);
}
熟悉java8源码的程序员写法

利用 java8 已经提供好的一些常用接口代替自己定义的接口Calculate

public class JAVA8Demo {

    public static void main(String[] args) {
        CalculateImpl calculate = new CalculateImpl();
        double a = 12, b = 3;
        double result = calculate.test((x, y) -> x + y, a, b);
        System.out.println("加法 a+b=" + result);
        double result2 = calculate.test((x, y) -> x - y, a, b);
        System.out.println("减法 a-b=" + result2);
        double result3 = calculate.test((x, y) -> x * y, a, b);
        System.out.println("乘法 a*b=" + result3);
        double result4 = calculate.test((x, y) -> x / y, a, b);
        System.out.println("除法 a/b=" + result4);

    }
}

class CalculateImpl {
    public double test(BiFunction<Double,Double,Double> biFunction, double a, double b) {
        return biFunction.apply(a, b);
    }
}

点评一下上面这些写法的优缺点

第一种写法,设计简单,通过传入操作符来判断使用那种运算,通过封装,将if-else封装好,但缺点也比较明显,if-else导致代码的可阅读性变差。而且高度耦合。如果要增加求余数等其它运算操作就得直接修改if-else分支源码,如果功能特别多,那么这个if-else就特别长,看都不想看的那种。

第二种写法,利用枚举代替if-else,这种写法会比if-else的代码阅读性会好一些,这些功能通过构造器被拆分出来,如果也要增加新功能,那么需要增加枚举的构造器,实现抽象方法来完成功能。优点是可以在其它类中复用代码,缺点就也是需要修改源码,具有一定的耦合性,但比第一种好。

第三种写法,利用函数式编程的思想,设计了一个接口,利用接口将计算后的结果值返回,为了进一步的优化,利用CalculateImpl.test()方法将接口进一步封装,而接口的实现则由lambda表达式完成。从代码量看出,这种写法的代码是最少的,也意味着java8的代码产出量是最大的。与写法一、写法二相比更具可变性,想要增加什么功能就能增加什么功能,缺点就是代码不容易复用,使用lambda表达式增加的功能不好在其它类中复用。也就是不能同对象.调用对应方法完成对应功能。

第四种写法,是熟悉java8源码的程序员才会的写法,没看过源码或者没见过这种写法的是不会想到这样写。

第四种写法引发了一个问题,你熟悉java8的源码?你知道java8提供了哪些常用函数式接口吗?

也就是下面这张截图,这些接口你知道怎么灵活使用?

文章末尾,希望这次案例能引发大家对源码的学习兴趣,要想写出好程序,是要熟悉jdk源码的。
如果你对jdk源码也很感兴趣。可以在我的 jdk源码专栏 或者 juc 专栏中找到一些我对jdk源码的阅读和对于源码的理解的相关文章。我欢迎大家的评论和指教,文章中如有错误的地方请指出,我会改正过来。

如果你喜欢这类文章,可以点个关注,我写博客的频率是比较高的,说不定有很多好文章在等着我们一起学习学习。

相关文章