对于Lamdba的引入

前言

	文章如有不足之处,敬请指出 。欢迎多多交流。本人看到,会第一时间回复信息的。

Lamdba表达式的引入

前言

在日常的开发过程中,我们能够唯一确定的就是对于需求总是在不停的变化的。而对于一个好的开发人员而言,我们所能做的,就是尽可能的让我们的代码适应尽可能的需求变化,对于每次需求的改动,尽可能的在减少开发量的同事,保持程序的稳定性和健壮性,以及对于未来的可扩展性性是我们追求的目标。

概述

在这篇文章中,我们并不会讲解lamdba表达式的具体用法和高级特性,而是讲解下为什么我们需要使用lamdba表达式,和用了lamdba表达式对于我们的好处是什么。

问题一

下面我们举个栗子,例如我们给一个果农做一个产品筛选系统,目前,这个果农仅仅只是售卖苹果这一种产品,而对于苹果而言,果农需要对于苹果进行进行不同的区分,苹果的颜色分为绿色、红色等,将所有的红颜色的苹果都筛选出来。

解决一

基于以上的例子,只要是会Java开发的人而言,我们可以迅速的进行代码开发,然后交给果农进行验证:
首先我们创建一个Apple的类

public class Apple{

    private final String color;

    private final  Double weight;

    private Apple(AppleBuilder appleBuilder){
        this.color = appleBuilder.getColor();
        this.weight = appleBuilder.getWeight();
    }

    public String getColor() {
        return color;
    }

    public Double getWeight() {
        return weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "color='" + color + '\'' +
                ", weight=" + weight +
                '}';
    }

    static class AppleBuilder{

        private String color;

        private Double weight;


        public AppleBuilder color(String color){
            this.color = color;
            return this;
        }

        public AppleBuilder weight(Double weight){
            this.weight = weight;
            return this;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public Double getWeight() {
            return weight;
        }

        public void setWeight(Double weight) {
            this.weight = weight;
        }

        public Apple build(){
            return new Apple(this);
        }

    }

}

然后如下解决问题:

import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class TestOne {

    public static final String APPLE_COLOR_RED_CODE="red";

    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList=Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(checkApple(apple)){
                System.out.println(apple);
            }
        }
    }

    private boolean checkApple(Apple apple){
        if(APPLE_COLOR_RED_CODE.equals(apple.getColor())){
            return true;
        }
        return false;
    }
}

问题二

很好,到目前为止,你已经完成了将系统中所有的红苹果都筛选出来的需求了。当你兴高采烈的将功能提供给果农用的时候,果农突然改变了注意,他不再想要红颜色的苹果了,而是想要绿颜色的苹果。

解决二

此时,你也许会觉得那我直接在代码中定义一个绿色的常量,然后将判断改成判断绿色不就可以了吗?
然后你迅速的将代码改成了如下的方式:

import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class TestOne {

    public static final String APPLE_COLOR_RED_CODE="red";

    public static final String APPLE_COLOR_GREEN_CODE="green";



    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList=Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(checkApple(apple)){
                System.out.println(apple);
            }
        }
    }

    private boolean checkApple(Apple apple){
        if(APPLE_COLOR_GREEN_CODE.equals(apple.getColor())){
            return true;
        }
        return false;
    }

}

当你写完这样的代码,你们的技术经理肯定会有这样的疑问?那如果果农突然又想要筛选红色的苹果呢?你难道再改回来吗?
带着这样的疑问,你开始了代码的第一版优化:

public class TestOne {

    public static final String APPLE_COLOR_RED_CODE="red";

    public static final String APPLE_COLOR_GREEN_CODE="green";



    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList=Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(checkAppleRed(apple)){
                System.out.println(apple);
            }
        }
    }

    private boolean checkAppleRed(Apple apple){
        if(APPLE_COLOR_RED_CODE.equals(apple.getColor())){
            return true;
        }
        return false;
    }

    private boolean checkAppleGreen(Apple apple){
        if(APPLE_COLOR_GREEN_CODE.equals(apple.getColor())){
            return true;
        }
        return false;
    }

}

此情此景,你觉得你可以满足用户的需求,当用户想要从查询红色苹果到绿色苹果,我们只需要替换一下调用的方法而已:可是,这真的最好的方法吗?我们写代码的根本目的在于让程序更加的通用,能够更加满足需求的变化,那如果以后摘选粉色的、白色的苹果呢?难道还需要添加一个新的方法吗?这样只能会让你的方法、你的代码越来越不受控制。拿到我们就没有更好的办法来解决这个问题了吗?
    此时,你可能回想,我可以把颜色这个作为一个参数传递进去,从而达到优化代码的作用:
    代码的第二版优化:

public class TestOne {

    public static final String APPLE_COLOR_RED_CODE="red";

    public static final String APPLE_COLOR_GREEN_CODE="green";



    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList= Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(checkApple(apple,APPLE_COLOR_RED_CODE)){
                System.out.println(apple);
            }
        }
    }

    private boolean checkApple(Apple apple,String appleColor){
        if(appleColor.equals(apple.getColor())){
            return true;
        }
        return false;
    }

ok,此时你认为终于找到了这个问题的通用方法。终于找到了最优解。no,you are wrong.此时果农又有了新的需求,他需要查找出红色的苹果,并且重量大于150g的苹果。显然,你目前的代码是肯定不支持的。如果你想要将重量也传进去,那无疑会对现在已有的代码起到了冲击(需要对于已经调用的地方都添加重量参数),并且也会让你的方法的参数也来越多。这显然不是一个很好的解决办法。
    因此,在这里,我们借鉴下设计模式中的"策略者模式"进行对代码第三版优化:
    我们通过上面的分析可以发现,真正变化的,其实是private boolean checkApple(Apple apple)这个方法,第一版优化,也仅仅只是在对于这个函数实现了不同的扩展而已。那么我们可以将这个方法抽象成一个接口–算法族,等到以后我们采用不同的筛选条件的时候,也只需要去实现这个接口,实现当前这个接口的方法而已–策略
    首先,我们定义一个接口:

public interface AppleCheckInterface {
    public static final String APPLE_COLOR_RED_CODE="red";

    public static final String APPLE_COLOR_GREEN_CODE="green";
    
    boolean checkApple(Apple apple);
}

在这个接口中,我们将对于苹果的筛选抽取出一个接口,然后我们对于红苹果、绿苹果的筛选,试下两个不同的实现类:
    第一个是筛选红苹果:

public class CheckRedApple implements AppleCheckInterface {
    @Override
    public boolean checkApple(Apple apple) {
       if(APPLE_COLOR_RED_CODE.equalsIgnoreCase(apple.getColor())){
           return true;
       }
       return false;
    }
}

第二个是筛选绿苹果:

public class CheckGreenApple implements  AppleCheckInterface {
    @Override
    public boolean checkApple(Apple apple) {
        if(APPLE_COLOR_GREEN_CODE.equalsIgnoreCase(apple.getColor())){
            return true;
        }
        return false;
    }
}

此时我们的筛选调用代码改为:

public class TestOne {

    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList= Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(checkApple(apple,new CheckRedApple())){
                System.out.println(apple);
            }
        }
    }

    private boolean checkApple(Apple apple, AppleCheckInterface appleCheckInterface){
        if(appleCheckInterface.checkApple(apple)){
            return true;
        }
        return false;
    }
}

此时,我们发现这种方式比之前的方式优美了很多,等到以后我来了一个新的筛选条件的时候,我也仅仅只是需要实现一个AppleCheckInterface接口,然后在调用的时候,传入适合的对象即可。同时,和上面不同的是,我们无需改动其他的类。减少了回归测试的成本。但是这种方式还是有一个问题,那就是每次实现一个筛选条件,就需要创建一个新的AppleCheckInterface接口的实现类。等待筛选条件特别多的时候,会出现实现类也超级多,不方便管理的情况。对于这种情况,我们可以采用JDK提供的另外一种方法–匿名内部类来实现,下面是第四版代码优化:

public class TestOne {

    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList= Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(checkApple(apple, new AppleCheckInterface() {
                @Override
                public boolean checkApple(Apple apple) {
                    if(APPLE_COLOR_RED_CODE.equals(apple.getColor())){
                        return true;
                    }
                    return false;
                }
            })){
                System.out.println(apple);
            }
        }
    }

    private boolean checkApple(Apple apple, AppleCheckInterface appleCheckInterface){
        if(appleCheckInterface.checkApple(apple)){
            return true;
        }
        return false;
    }
}

在这里,我们采用匿名内部类的方式,来替代了我们AppleCheckInterface接口的实现类,但是匿名内部类有个致命的缺陷,就是十分不利于代码的阅读。当前是因为程序的简单,可能你还能读得懂这个匿名内部类的意思,一旦这个接口是一个超级复杂的接口,直接回导致你崩溃掉。那就没有更好的方式来实现了吗?
    在上面我们分析到,其实真正有用的代码就是你的那个判断而已,其他都是为了让这句话的语法通顺和符合JDK的标准而写的样板代码而已。那么我们可不可以有这样的一个函数,将这个判断像是参数一样传递进去,然后直接返回我们想要的结果呢?
    不用担心,在JDK1.8中已经支持了你的这个想法,那就是–“行为参数化”
    下面我们直接使用JDK1.8中的Stream流和lamdba来实现这个需求,至于这两个功能,会在后面的文章中进行介绍,此处仅仅只是体验先JDK1.8的Stream流和lamdba表达式给我们带来的便捷:
    首先,我们先定义一个接口:

public interface Predicate<T> {

    boolean test(T t);

    static <T> boolean check(T t,Predicate<T> p){
        if(p.test(t)){
            return true;
        }
        return false;
    }

}

然后看调用方法:

public class TestOne {

    public static final String APPLE_COLOR_RED_CODE="red";

    public static final String APPLE_COLOR_GREEN_CODE="green";

    private List<Apple> appleList;

    @Before
    public void prepare(){
        appleList= Arrays.asList(new Apple.AppleBuilder().color("red").weight(12.0).build(),new Apple.AppleBuilder().color("green").weight(9.8).build(),new Apple.AppleBuilder().color("red").weight(6.6).build());
    }

    @Test
    public void testOne(){
        for(Apple apple : appleList){
            if(Predicate.check(apple,a -> APPLE_COLOR_RED_CODE.equals(a.getColor()))){
                System.out.println(apple);
            }
        }
    }
}

此时我们看到,原来啰嗦的第一版、第二版、第三版、第四版代码直接由现在的一行代码搞定。这个就是JDK1.8Stream流和lamdba表达式给我们带来的便捷与优越。
    在这里,不用去考虑程序的效率问题,后期会有一片单独的文章来讲解lamdba的性能问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值