假設(shè)現(xiàn)在我有個(gè)需求,把1個(gè)數(shù)組里的整數(shù)值進(jìn)行由低到高的排序
我可以先寫1個(gè)工具類Sorter出來,然后其他類new 調(diào)用它的方法
//這里使用的是選擇排序
public class Sorter {
public void sort(int[] arr){
for (int i = 0; i < arr.length - 1; i++) {
int minPos = i;
for (int j = i+1; j < arr.length; j++) {
minPos = arr[j] < arr[minPos]? j : minPos;
}
swap(arr,i,minPos);
}
}
static void swap(int[] arr,int i,int j){
int temp;
temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
當(dāng)我們其他類需要實(shí)現(xiàn)排序功能時(shí) 就調(diào)用這個(gè)類里的方法
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] a = {9,2,3,15,24,1,41};
Sorter sorter = new Sorter();
sorter.sort(a);
System.out.println(Arrays.toString(a));
}
}

現(xiàn)在我的需求變更了, 如果我想對(duì)double類型進(jìn)行排序怎么辦?
我可以對(duì)Sorter類里面的sort方法進(jìn)行重載
//數(shù)組類型變成double
public class Sorter {
public void sort(double[] arr){
...里面的比較邏輯不變
}
static void swap(double[] arr,int i,int j){
double temp;
..交換邏輯不變
}
}
如果我們還需要對(duì)float,short,long進(jìn)行排序呢?都要重新寫一篇嗎?
我們發(fā)現(xiàn),按照重載的方法,我們需要把1個(gè)大部分都相同的算法,就因?yàn)?個(gè)類型的變化,來來回回寫好多遍

而且因?yàn)槟銓懰懒吮容^的標(biāo)準(zhǔn),你也不能通過傳入Object[] arr的方式來泛化,因?yàn)镺bject[] arr是無法用<符號(hào)來比較大小的,會(huì)報(bào)錯(cuò)
怎么辦呢?我傳入Object[] 然后讓對(duì)象的類去實(shí)現(xiàn)標(biāo)準(zhǔn)?(后面會(huì)講)
怎么辦呢?策略模式就是我們不關(guān)注算法的具體過程 前提這個(gè)算法有1個(gè)唯一的標(biāo)準(zhǔn)(我們發(fā)現(xiàn)只要計(jì)算出來了 哪個(gè)值小于哪個(gè)值 只要確定好了這個(gè)比較標(biāo)準(zhǔn)minPos = arr[j] < arr[minPos]? j : minPos; 這個(gè)算法就可以大部分成立) 然后我們把這個(gè)標(biāo)準(zhǔn)外包給對(duì)象的類里面去實(shí)現(xiàn)
上面問題 同樣適用于對(duì)象里的比較排序,但是這個(gè)對(duì)象里排序的標(biāo)準(zhǔn),是不確定的,
那么既然我們不知道Sorter里的標(biāo)準(zhǔn)到底是哪個(gè)?那么我就把Sorter里面這個(gè)標(biāo)準(zhǔn)實(shí)現(xiàn)的動(dòng)作 交給其他人 創(chuàng)建對(duì)象的類(這里比如 我創(chuàng)建1個(gè)Cat類型 Cat里面2個(gè)屬性weight,heigth)里面去實(shí)現(xiàn),我只是調(diào)用1個(gè)CompareTo方法 具體的實(shí)現(xiàn)外包給對(duì)象的類 (達(dá)到策略模式的一半)
所以Sorter里的代碼 就變成了
public class Sorter<T> {
public void sort(Cat[] arr){
for (int i = 0; i < arr.length - 1; i++) {
int minPos = i;
for (int j = i+1; j < arr.length; j++) {
//通過對(duì)象的compareTo方法 告訴我誰小誰大
minPos = arr[j].compareTo(arr[minPos])==-1 ? j : minPos;
}
swap(arr,i,minPos);
}
}
....后面swap方法實(shí)現(xiàn)省略
假如我們的比較標(biāo)準(zhǔn)是Cat的weigth
我們?cè)贑at中可以實(shí)現(xiàn)1個(gè)比較標(biāo)準(zhǔn)的方法CompareTo,比較他們的weigth
public class Cat {
int weight,height;
public Cat(int weight,int height){
this.weight = weight;
this.height = height;
}
//自己要在類中定義1個(gè)比較的標(biāo)準(zhǔn)CompareTo
public int compareTo(Cat c){
if(this.weight < c.weight) return -1;
else if(this.weight > c.weight) return 1;
else return 0;
}
//因?yàn)?Arrays.toString(Cat[] a)
//其實(shí)toString最后調(diào)用的是Cat類里的自己toString方法
//所以為了方便 打印進(jìn)行重寫
@Override
public String toString() {
return "Cat{" +
"weight=" + weight +
", height=" + height +
'}';
}
}
Main創(chuàng)建Cat并賦值 并且利用Sorter工具類進(jìn)行對(duì)Cat排序
public class Main {
public static void main(String[] args) {
Cat[] a = {new Cat(1, 2), new Cat(9, 2),
new Cat(6, 2), new Cat(5, 2)};
Sorter sorter = new Sorter();
sorter.sort(a);
System.out.println(Arrays.toString(a));
}}

利用這種"外包標(biāo)準(zhǔn)實(shí)現(xiàn)"的策略思想,這樣我就不僅可以對(duì)8種基本數(shù)據(jù)類型的包裝類(注意:不是byte、short、int、long、float、double、boolean、char 基本數(shù)據(jù)類型 而是Byte Short Integer Long Float Double Character Boolean包裝類 因?yàn)橹挥袑?duì)象里面才可以實(shí)現(xiàn)CompareTo方法)進(jìn)行排序外 還可以對(duì)對(duì)象進(jìn)行排序

需求又來了:如果我還有個(gè)狗,企鵝,雞等類 這些類里面也要實(shí)現(xiàn) 這些代碼需要在Sorter類里面再重載寫1遍 就像上面int[],double[]的比較嗎? 不需要了,因?yàn)槎际菍?duì)象了,我們改成傳入object類型,因?yàn)閖ava里面一切皆對(duì)象,object是一切對(duì)象的父類,在我們用包裝類的情況下,一切都解決了,在自己對(duì)象的類里面的CompareTo方法里 自己進(jìn)行強(qiáng)轉(zhuǎn)就行了
這樣想有問題嗎? 其實(shí)是有問題的,因?yàn)镾ort傳Object類型的話,Object類型里面是沒有CompateTo方法的 所以你的比較標(biāo)準(zhǔn)還是有問題的

所以我們傳1個(gè)Comparable接口(對(duì)象)數(shù)組,我們要保證,這個(gè)Comparable接口里面一定有CompareTo方法,然后上面的arr[j].compareTo標(biāo)準(zhǔn)才不會(huì)出錯(cuò)
所以我們?cè)傩陆?個(gè)接口叫做Comparable 它里面有個(gè)抽象方法compareTo(Object o)
public interface Comparable {
//注意 這里CompareTo傳入的類型一定不能寫死
//寫死了 那么其他類就無法去實(shí)現(xiàn)自己comparaTo方法 定制自己類的比較標(biāo)準(zhǔn)了
int compareTo(Object o);
}
于是Sorter類最終成了
public class Sorter<T> {
public void sort(Comparable[] arr){
for (int i = 0; i < arr.length - 1; i++) {
int minPos = i;
for (int j = i+1; j < arr.length; j++) {
minPos = arr[j].compareTo(arr[minPos])==-1 ? j : minPos;
// minPos = (arr[j] < arr[minPos]) ? j : minPos;
}
swap(arr,i,minPos);
}
}
//這里的Object[] arr 改成Comparable[] arr也可以
static void swap(Object[] arr, int i, int j){
Object temp = arr[i];
arr[i]=arr[j];
arr[j] = temp;
}
}
然后Cat等類 如果你想排序的話 就去實(shí)現(xiàn)Comparable接口 實(shí)現(xiàn)自己的比較標(biāo)準(zhǔn)compareTo(object類型) 才能調(diào)用Sorter里的sort方法
public class Cat implements Comparable {
int weight,height;
public Cat(int weight,int height){
this.weight = weight;
this.height = height;
}
//自己要在類中定義1個(gè)比較的標(biāo)準(zhǔn)CompareTo
public int compareTo(Object o){
//實(shí)現(xiàn)compareTo接口 強(qiáng)制轉(zhuǎn)為 自己的類的對(duì)象
Cat c = (Cat)o;
if(this.weight < c.weight) return -1;
else if(this.weight > c.weight) return 1;
else return 0;
}
//因?yàn)?Arrays.toString(Cat[] a)
//其實(shí)toString最后調(diào)用的是Cat類里的自己toString方法
//所以為了方便 打印進(jìn)行重寫
@Override
public String toString() {
return "Cat{" +
"weight=" + weight +
", height=" + height +
'}';
}
}
現(xiàn)在Main方法不用改變
public class Main {
public static void main(String[] args) {
Cat[] a = {new Cat(189, 2), new Cat(9, 2),
new Cat(6, 2), new Cat(5, 2)};
Sorter sorter = new Sorter();
sorter.sort(a);
System.out.println(Arrays.toString(a));
}
}

同理再來個(gè)Dog,Panda,F(xiàn)rog也是一樣的
//要排序,就要實(shí)現(xiàn)Comparable接口
public class Dog implements Comparable {
int age,size;
public Dog(int age,int size){
this.age = age;
this.size = size;
}
//還必須要實(shí)現(xiàn) 比較的標(biāo)準(zhǔn)
public int compareTo(Object o){
Dog d = (Dog)o;
if(this.age < d.age) return -1;
else if(this.age > d.age) return 1;
else return 0;
}
@Override
public String toString() {
return "Dog{" +
"age=" + age +
", size=" + size +
'}';
}
}
Main方法的調(diào)用是基本不變的
public class Main {
public static void main(String[] args) {
// Cat[] a = {new Cat(189, 2), new Cat(9, 2),
// new Cat(6, 2), new Cat(5, 2)};
Dog[] a = {new Dog(189, 2), new Dog(9, 2),
new Dog(6, 2), new Dog(5, 2)};
Sorter sorter = new Sorter();
sorter.sort(a);
System.out.println(Arrays.toString(a));
}
}
優(yōu)化:如果我們寫Cat類的時(shí)候,如果需要調(diào)用一下compareTo()方法,但是不小心傳進(jìn)入的不是Cat類的對(duì)象,那么compareTo里面執(zhí)行強(qiáng)制轉(zhuǎn)換(Cat)o直接就報(bào)錯(cuò)了,這樣寬進(jìn)嚴(yán)出是不是有一點(diǎn)風(fēng)險(xiǎn),所以我們可以加個(gè)泛型<T>,指定你進(jìn)來的必須是我指定的類型<T> ,不然我編譯的時(shí)候就報(bào)錯(cuò)
那么我們需要把Comparable接口改一下 Object改為泛型
public interface Comparable<T> {
int compareTo(T o);
}
然后Cat實(shí)現(xiàn)接口的時(shí)候就指定好Comparable里面的類
//泛型接口
public class Cat implements Comparable<Cat> {
int weight,height;
public Cat(int weight,int height){
this.weight = weight;
this.height = height;
}
//自己要在類中定義1個(gè)比較的標(biāo)準(zhǔn)CompareTo
public int compareTo(Cat o){
// Cat c = (Cat)o; 不需要強(qiáng)制轉(zhuǎn)換了
if(this.weight < c.weight) return -1;
else if(this.weight > c.weight) return 1;
else return 0;
}
....其他省略
}
}
好的,現(xiàn)在我們總結(jié)一下,現(xiàn)在的情況
我們有1個(gè)Sorter工具類,它里面實(shí)現(xiàn)了排序方法Sort(Comparable[] arr),不過這個(gè)方法必須傳入的參數(shù)是Comparable接口數(shù)組
我們?nèi)绻袑?duì)象數(shù)組要實(shí)現(xiàn)排序的功能的話,必須要去實(shí)現(xiàn)Comparable<T>接口,在compareTo() 里實(shí)現(xiàn)自己排序的依據(jù)
然后我們Main要對(duì)對(duì)象數(shù)組進(jìn)行排序的時(shí)候 就直接new出來 然后sort(arr)就完了
現(xiàn)在有來了個(gè)需求:我現(xiàn)在不想對(duì)Cat.weight進(jìn)行比較了,而想對(duì)Cat.height進(jìn)行比較,怎么辦?
我們已經(jīng)把Cat.compareTo(Cat o)方法實(shí)現(xiàn)了 而且現(xiàn)在不是改變的傳入?yún)?shù),而是里面的邏輯,不能重寫或重載方法,所以要改變的話,需要修改Cat.compareTo(Cat o)里面的源碼,這十分麻煩,而且一般類的屬性不只兩個(gè),有時(shí)候我們經(jīng)常要改變比較準(zhǔn)備,經(jīng)常改源碼,會(huì)累死人,而且修改線上源碼也不合適,根據(jù)開閉原則,“軟件中的對(duì)象(類,模塊,函數(shù)等等)應(yīng)該對(duì)于擴(kuò)展是開放的,但是對(duì)于修改是封閉的”,怎么辦呢?
我們把比較標(biāo)準(zhǔn)Comparable接口的代碼 單獨(dú)從類的源碼里抽出來,不在類的源碼里實(shí)現(xiàn)了,把源碼里的變成比較器Comparator而不是具體的比較標(biāo)準(zhǔn),當(dāng)我們用的時(shí)候,自己寫比較標(biāo)準(zhǔn)器Comparator傳到Sorter里去調(diào)用它的compareTo()方法,這個(gè)compareTo()是我們?cè)谕饷婢涂梢孕薷牡谋容^策略,不同的比較策略都可以按照自己的想法實(shí)現(xiàn),這樣就更好了
所以我新建1個(gè)比較器Comparator接口
public interface Comparator<T> {
//因?yàn)槭菑脑创a類里面剝離出來的比較器
//所以我們使用Comparator的時(shí)候
//this指針指向不是對(duì)象的類本身 不能this.weight < c.weight了
//所以我們需要傳2個(gè)比較的對(duì)象參數(shù)o1 o2才能比較
int compare(T o1,T o2);
}
比如,我現(xiàn)在按照Dog.age對(duì)象數(shù)組進(jìn)行排序,我們就需要自己創(chuàng)建1個(gè)類,DogAgeComparator比較器,它要實(shí)現(xiàn)比較器,然后自己實(shí)現(xiàn)它的標(biāo)準(zhǔn)
public class DogAgeComparator implements Comparator<Dog> {
public int compare(Dog d1,Dog d2){
if(d1.age<d2.age)return -1;
else if(d1.age>d2.age) return 1;
else return 0;
}
}
然后我們也需要修改1下Sorter類的源碼
傳入的參數(shù)是2個(gè): 1個(gè)是對(duì)象數(shù)組 1個(gè)就是我們定義的比較策略器類
public class Sorter<T> {
public void sort(T[] arr, Comparator<T> comparator){
for (int i = 0; i < arr.length - 1; i++) {
int minPos = i;
for (int j = i+1; j < arr.length; j++) {
//自定義的比較策略器comparator
minPos = comparator.compare(arr[j],arr[minPos])==-1? j : minPos;
}
swap(arr,i,minPos);
}
}
.......
}
Main方法 創(chuàng)建Dog數(shù)組對(duì)象 然后new出Sorter工具類,然后創(chuàng)建自己寫的比較策略器,把對(duì)象數(shù)組和比較器 傳到sort方法里
public class Main {
public static void main(String[] args) {
Dog[] a = {new Dog(189, 6), new Dog(9, 2),
new Dog(6, 2), new Dog(5, 5151)};
Sorter sorter = new Sorter();
DogAgeComparator comparator=new DogAgeComparator() ;
sorter.sort(a,comparator);
System.out.println(Arrays.toString(a));
}
}

那么現(xiàn)在 我們不僅可以往Sorter里面?zhèn)鞲鞣N各樣的類型 而且還可以讓我們不斷擴(kuò)展自己的比較策略 可以體重 可以身高 也可以體重乘以身高 比comparable的寫法擴(kuò)展性好得多

這樣我要擴(kuò)展對(duì)象的排序的話 我sort方法是不用變的 需要變的只是 添加新的策略 Cat3C 然后實(shí)現(xiàn)comparator 然后傳進(jìn)去 就自動(dòng)幫我們排好序了
這種寫法,其實(shí)就是jdk中原生的java.util.Comparator類的實(shí)現(xiàn)邏輯,它里面也有個(gè)compare(T,T)方法和我們實(shí)現(xiàn)的compareTo(T,T)方法一樣的,這個(gè)Comparator其實(shí)用的就是策略模式,用原類的對(duì)象里剝離出來的自定義策略
優(yōu)化:我們實(shí)現(xiàn)自己比較策略器CompareTor時(shí),我們可以不"創(chuàng)建"類,用lamda表達(dá)式來寫,因?yàn)槲覀兊慕涌诶锩嬷挥?個(gè)方法CompareTo
public class Main {
public static void main(String[] args) {
Dog[] a = {new Dog(189, 6), new Dog(9, 2),
new Dog(6, 2), new Dog(5, 5151)};
//Sorter里面一定要指定<Dog> 因?yàn)楹竺鎠ort(T[],ComparaTor<T>)不知道到底是什么類的策略方法
Sorter<Dog> sorter = new Sorter();
sorter.sort(a,(o1,o2)->{
if(o1.size < o2.size) return -1;
else if(o1.size > o2.size) return 1;
else return 0;
});
System.out.println(Arrays.toString(a));
}
}
這個(gè)也被稱為 函數(shù)式接口 functional interface 接口里面只有1個(gè)方法
是函數(shù)式接口的 我們還可以聲明 它是1個(gè)@functional interface 但是我們確認(rèn) 它里面只有1個(gè)方法 不寫functional interface也可以
//函數(shù)式接口聲明
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1,T o2);
}
jdk1.8后接口有個(gè)特性,interface里面可以寫具體邏輯實(shí)現(xiàn)default void func(){邏輯處理;}
為什么接口里必須有方法實(shí)現(xiàn)? 因?yàn)樗С謑ambda表達(dá)式
根據(jù)開閉原則,之前的代碼都沒有寫default的 所以為了向前兼容 只用實(shí)現(xiàn)抽象方法 不實(shí)現(xiàn)defalut方法是可以的
public interface Comparator<T> {
int compare(T o1,T o2);
static void m123(){
System.out.println("m123");
}
default void m(){
System.out.println("m");
}
}
接口Comparator里的方法m123可以直接調(diào)用
public class DogAgeComparator implements Comparator<Dog> {
public int compare(Dog d1,Dog d2){
if(d1.age<d2.age)return -1;
else if(d1.age>d2.age) return 1;
else return 0;
}
public static void main(String[] args) {
Comparator.m123();
}
}

回顧:
認(rèn)識(shí)Comparable接口和Comparator比較器
jdk中 Comparable接口在java.lang.Comparable包中;Comparator類在java.util.Comparator包中 ;Comparable不是策略模式 Comparator是 通過實(shí)現(xiàn)子類來指定兩個(gè)對(duì)象之間如何比較大小的方式
它是一種策略模式
而一般策略模式,我們可以有很多種實(shí)現(xiàn)類,我們可以寫好,然后在配置文件中指定,靈活選擇,自己的標(biāo)準(zhǔn)