透过源码学习设计模式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); } }
与状态模式区别