一、前期准备

1.员工实体类

package StrategyMode;

/**
 * @Author: wjun
 * @Date: 2020/5/12 11:23
 * @Description: 员工实体类
 */
public class Employee {
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

2.需求

  • 打印出年龄超过35岁的员工信息
  • 打印出工资超过5000的员工信息

3.准备数据

public class Demo {
    private List<Employee> list = Arrays.asList(
            new Employee("张三", 20, 9999.99),
            new Employee("李四", 35, 6666.66),
            new Employee("王五", 50, 3333.33),
            new Employee("赵六", 18, 5555.55),
            new Employee("田七", 30, 7777.77)
    );
}

二、实现逻辑

通常可以想到通过遍历list里的元素,并通过get方法判断即可,因此

实现第一个需求代码如下:

@Test
public void test1() {
    for (Employee emp : list) {
        if (emp.getAge() >= 35) {
            System.out.println(emp);
        }
    }
}

实现第二个需求代码如下:

@Test
public void test2() {
    for (Employee emp : list) {
        if (emp.getSalary() >= 5000) {
            System.out.println(emp);
        }
    }
}

我们发现两个需求的代码几乎一样,只是修改了条件,产生大量的冗余代码,因此考虑优化代码

三、优化方法一:策略模式

1.简介

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改,在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

  • 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
  • 主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护
  • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为
  • 如何解决:将这些算法封装成一个一个的类,任意地替换
  • 关键代码:实现同一个接口

2.代码实现

a).定义一个策略的泛型接口

package StrategyMode;

/**
 * @Author: wjun
 * @Date: 2020/5/12 11:36
 * @Description:
 */
public interface MyStrategy<T> {
    public abstract boolean filter(T t);
}

b).根据逻辑定义我们的策略实现MyStrategy接口

实现第一个需求策略如下:

package StrategyMode;

/**
 * @Author: wjun
 * @Date: 2020/5/12 11:37
 * @Description:
 */
public class FilterByAge implements MyStrategy<Employee> {

    @Override
    public boolean filter(Employee employee) {
        return employee.getAge() >= 35;
    }
}

实现第二个需求策略如下:

package StrategyMode;

/**
 * @Author: wjun
 * @Date: 2020/5/12 11:45
 * @Description:
 */
public class FilterBySalary implements MyStrategy<Employee> {
    @Override
    public boolean filter(Employee employee) {
        return employee.getSalary() >= 5000;
    }
}

这样我们的需求实现如下:

private List<Employee> filterDemo(List<Employee> list, MyStrategy<Employee> ms) {
        List<Employee> tempList = new ArrayList<>();
        for (Employee emp : list) {
            if (ms.filter(emp)) {
                tempList.add(emp);
            }
        }

        return tempList;
    }

通过一个方法传入不同的策略MyStrategy<Employee>即可实现不同的逻辑,相当于将最开始的代码进行抽取,增强了程序的可读性。

具体需求实现的代码如下:

@Test
public void test3() {
    List<Employee> employeeList = filterDemo(list, new FilterByAge());
    for (Employee emp : employeeList) {
        System.out.println(emp);
    }
}

@Test
public void test4() {
    List<Employee> employeeList = filterDemo(list, new FilterBySalary());
    for (Employee emp : employeeList) {
        System.out.println(emp);
    }
}

但是策略模式的缺点也很明显,比如当我们增加策略,就需要创建更多的类来实现MyStrategy接口,因此还可以继续优化

四、优化方法二:匿名内部类

通过匿名内部类的形式创建MyStrategy接口的实现类,不需要再独立的创建.java文件,代码如下:

 @Test
public void test5() {
    List<Employee> employeeList1 = filterDemo(list, new MyStrategy<Employee>() {
        @Override
        public boolean filter(Employee employee) {
            return employee.getSalary() >= 5000;
        }
    });

    List<Employee> employeeList2 = filterDemo(list, new MyStrategy<Employee>() {
        @Override
        public boolean filter(Employee employee) {
            return employee.getAge() >= 35;
        }
    });

    for (Employee emp : employeeList1) {
        System.out.println(emp);
    }
    System.out.println("-----------------------------");
    for (Employee emp : employeeList2) {
        System.out.println(emp);
    }
}

只需要在调用filterDemo方法时,通过匿名内部类实现其中filter方法的形式传入参数,是不是很方便???但又出现了一些问题,其实我们匿名内部类真正有效的代码只有一行return employee.getSalary() >= 5000;为了这一行代码我们写了很多辅助性质的代码,为此我们还可以通过另一种方式来简化。

五、优化方法三:Lambda表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。我们可以通过Lambda表达式简化匿名内部类(匿名累不累♪(^∀^●)ノ),代码如下:

@Test
public void test6() {
    List<Employee> employeeList1 = filterDemo(list, (e) -> e.getAge() >= 35);
    List<Employee> employeeList2 = filterDemo(list, (e) -> e.getSalary() >= 5000);
    employeeList1.forEach(System.out::println);
    System.out.println("-----------------------------");
    employeeList2.forEach(System.out::println);
}

将上面一串代码简化成两行代码,当然对于按代码行数算钱的程序员不太友好。在jdk1.8引入了流的概念,通过Stream API我们只需要一个存有员工信息的容器就可以实现上述两个需求。

六、优化方法四:Stream API

代码实现如下:

@Test
public void test7() {
    list.stream()
        .filter((e) -> e.getAge() >= 35)
        .forEach(System.out::println);

    System.out.println("-----------------------------");

    list.stream()
        .map(Employee::getName)
        .forEach(System.out::println);
}

Stream API有很多可玩的东西,极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码和最近学习的scala很像。


只喜欢学习