透過源碼學習設計模式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); } }
與狀態模式區別