透过源码学习设计模式6—策略模式与Comparator

  • 2019 年 10 月 6 日
  • 笔记

简介:

定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。它首先定义不同的算法策略,然后客户端把算法策略作为它的一个参数。使用这种模式的一个不错的例子是Collection.sort()方法了,它使用Comparator对象作为参数。根据Comparator接口不同实现,对象会被不同的方法排序。

角色:

环境(Context):用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;

抽象策略角色(Strategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现

具体策略角色(ConcreteStrategy):实现了抽象策略定义的接口,提供具体的算法实现。

原则:

1、找出应用中可能需要变化之处,把他们独立出来。

2、针对接口编程,而不是针对实现

3、多用组合,少用继承

一般使用场景:

当系统能在几种算法中快速地切换,或系统中有一些类,它们仅行为不同时,或系统中存在多重条件选择语句时,可以考虑采用策略模式。

优缺点:

优点:

1、算法可以自由切换

2、避免使用多重条件判断

3、扩展性良好

缺点:

策略类数量增多,所有的策略类都需要对外暴露(不过可以通过其他如工厂方法模式修复这个缺陷)

JDK示例:

java.util.Collections方法:

sort(List<T> list, Comparator<? super T> c)

参数:

list – 要排序的列表。

c – 确定列表顺序的比较器。null 值指示应该使用元素的 自然顺序。

根据指定Comparator产生的顺序对指定列表进行排序。此列表内的所有元素都必须能够使用指定Comparator相互比较。sort()方法是常用的策略设计模式示例之一,它以Comparator作为参数,我们可以为Comparator接口提供不同的实现。现在,Collections.sort()方法将根据传递给它的比较器实现对象排序。比如根据体重或大小来对Employee进行排序,您可以简单地创建一个Comparator来实现。

我们先来看下可与之对比的Comparable接口, 它为类的对象定义了默认顺序。这种默认顺序也称为对象的自然顺序。

class Employee implements Comparable<Employee> {        private int id;        private String name;        private double salary;        private LocalDate joiningDate;          public Employee(int id, String name, double salary, LocalDate joiningDate) {            this.id = id;            this.name = name;            this.salary = salary;            this.joiningDate = joiningDate;        }          public int getId() {            return id;        }          public void setId(int id) {            this.id = id;        }          public String getName() {            return name;        }          public void setName(String name) {            this.name = name;        }            public double getSalary() {            return salary;        }            public void setSalary(double salary) {            this.salary = salary;        }          public LocalDate getJoiningDate() {            return joiningDate;        }          public void setJoiningDate(LocalDate joiningDate) {            this.joiningDate = joiningDate;        }            // 根据ID进行比较        /**         * @param   anotherEmployee - The Employee to be compared.         * @return  A negative integer, zero, or a positive integer as this employee         *          is less than, equal to, or greater than the supplied employee object.        */        @Override        public int compareTo(Employee anotherEmployee) {            return this.getId() - anotherEmployee.getId();        }          // Two employees are equal if their IDs are equal        @Override        public boolean equals(Object o) {            if (this == o) return true;            if (o == null || getClass() != o.getClass()) return false;            Employee employee = (Employee) o;            return id == employee.id;        }          @Override        public int hashCode() {            return Objects.hash(id);        }          @Override        public String toString() {            return "Employee{" +                    "id=" + id +                    ", name='" + name + ''' +                    ", salary=" + salary +                    ", joiningDate=" + joiningDate +                    '}';        }    }

但是,如果您因为某个需求更改默认顺序呢?例如,如果您想根据前面示例中的Employee对象的名称而不是id对其进行排序,该怎么办?

你不能更改compareTo()函数的实现,因为不单单是你那块的特定需求, 它会影响所有地方的顺序。

此外,如果您正在处理预定义的Java类或在第三方库中定义的类,则无法更改默认顺序。例如,字符串对象的默认顺序是按字母顺序排列。但是如果你想根据它们的长度来排序呢?

对于这种情况,Java提供的正是Comparator接口。您可以定义一个Comparator并将其传递给像Collections.sort() 或 Arrays.sort()的排序函数。根据Comparator定义的顺序对对象排序。

public interface Comparator<T> {        int compare(T o1, T o2);    }        public class ComparatorExample {        public static void main(String[] args) {            List<Employee> employees = new ArrayList<>();              employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10)));            employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19)));            employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28)));            employees.add(new Employee(1009, "Steve", 100000.00, LocalDate.of(2016, 5, 18)));                System.out.println("Employees : " + employees);              // 根据名称排序            Comparator<Employee> employeeNameComparator = new Comparator<Employee>() {                @Override                public int compare(Employee e1, Employee e2) {                    return e1.getName().compareTo(e2.getName());                }            };            /*            上面的Comparator可以用如下的lambda表达式写 =>            employeeNameComparator = (e1, e2) -> e1.getName().compareTo(e2.getName());            Java 8 Comparator默认方法甚至可以写的更简单            employeeNameComparator = [Comparator.comparing(Employee::getName](http://Comparator.comparing(Employee::getName))            */            Collections.sort(employees, employeeNameComparator);            System.out.println("nEmployees (Sorted by Name) : " + employees);            // 根据收入排序            Comparator<Employee> employeeSalaryComparator = new Comparator<Employee>() {                @Override                public int compare(Employee e1, Employee e2) {                    if(e1.getSalary() < e2.getSalary()) {                        return -1;                    } else if (e1.getSalary() > e2.getSalary()) {                        return 1;                    } else {                        return 0;                    }                }            };              Collections.sort(employees, employeeSalaryComparator);            System.out.println("nEmployees (Sorted by Salary) : " + employees);              //根据入职时间排序            Comparator<Employee> employeeJoiningDateComparator = new Comparator<Employee>() {                @Override                public int compare(Employee e1, Employee e2) {                    return e1.getJoiningDate().compareTo(e2.getJoiningDate());                }            };              Collections.sort(employees, employeeJoiningDateComparator);            System.out.println("nEmployees (Sorted by JoiningDate) : " + employees);        }    }

与状态模式区别