話說當年東吳孫權爲劉備借走了荊州不還而耿耿於懷,卻不料甘夫人去世,周郎頓時計上心來,讓孫權將其妹嫁與劉備,騙劉備來東吳完婚。劉備又不是傻子,當然知道其中的貓膩,當即表態:打死也不去。諸葛亮卻說無妨,當下給了趙雲三個錦囊妙計,讓他陪劉備去東吳完婚……最後的結果大家都知道,趙雲在恰當的時候一一打開三個錦囊妙計,從而將危機一一化解。周瑜只落了個“周郎妙計安天下,賠了夫人又折兵”的笑柄。
這個故事,我們感興趣的是三個錦囊妙計。諸葛亮一定是運用策略模式的高手,我們想啊:諸葛亮爲什麼要這麼麻煩,做三個錦囊,他完全可以只做一個錦囊,將這三個妙計都寫在它上面。可他沒有這麼做,而是正確的運用了策略模式做了三個錦囊。這樣做的好處十分明顯:諸葛亮一個錦囊寫一個妙計,他的思路十分清晰,不會三個計策相互混亂。而趙雲看妙計的時候也十分方便,什麼時候看哪個妙計,使用十分方便,如果三個妙計混在一起,他就沒這麼方便了。
現在看來,這個故事正包含了策略模式的解決問題的思路:解決某一問題有多種方法或算法,如諸葛亮給了三個妙計,類的提供者要將這多種方法或算法獨立開來,如諸葛亮將三個妙計分三個錦囊裝,類的使用者,如趙雲,在相應的條件,如故事中的恰當時間,使用相應的方法或算法。
前面我們說過命令模式,命令模式是對行爲的封裝;策略模式其實跟它的解決思路一樣,是對算法的封裝。當然,它們都一樣的擁有同一個接口。
下面我們以一些例子來詳細的說一說策略模式。
首先的一個例子是我們對數組的排序,對數組的排序有很多算法,我們想做一個由用戶指定一個整形數組和對它排序的算法,我們給出結果這樣一個方法,我們可以這樣做:
public int[] sort(int[] inArray,String type)
{
if(type.equals(“a”))
{
int k = 0;
for(int i=0;i<inArray.length;i++)
{
for(int j=i+1;j<inArray.length;j++)
{
if(inArray[i]> inArray[j])
{
k = inArray[i];
inArray[i] = inArray[j];
inArray[j] = k;
}
}
}
}
else if(type.equals(“b”))
{
int k = 0;
for(int i=0;i< inArray.length;i++)
{
for(int j=0;j< inArray.length-i-1;j++)
{
if(inArray [j]> inArray [j+1])
{
k = inArray [j];
inArray [j] = inArray [j+1];
inArray [j+1] = k;
}
}
}
}
}
一看上面的代碼,大家就覺得問題很多:第一,所有的算法都攪和在一起,不滿足單一職責原則。第二,依賴細節,不滿足依賴顛倒原則。第三,擴展性不好,如果要增加一個新的算法,需要對該方法進行改動。
下面我們來看看策略模式的解決思路。
首先是對所有的算法抽象一個接口,這是爲了滿足擴展性的基本條件,幾乎所有的模式都要以接口爲基礎:
public interface SortAlgorithm
{
public void sort(int[] inArr);
}
然後是各個算法的具體實現,它們實現了SortAlgorithm接口,完成了對關注點的分離,滿足單一職責原則:
public class SortOne implements SortAlgorithm
{
public void sort(int[] inArr)
{
int k = 0;
for(int i=0;i<inArr.length;i++)
{
for(int j=i+1;j<inArr.length;j++)
{
if(inArr[i]> inArr[j])
{
k = inArr[i];
inArr[i] = inArr[j];
inArr[j] = k;
}
}
}
}
}
public class SortTwo implements SortAlgorithm
{
public void sort(int[] inArr)
{
int k = 0;
for(int j=0;j< inArr.length-i-1;j++)
{
if(inArr[j]> inArr[j+1])
{
k = inArr[j];
inArr[j] = inArr[j+1];
inArr[j+1] = k;
}
}
}
}
然後我們來看客戶端的調用:
public int[] sort(int[] inArray,String type)
{
SortAlgorithm sa;
if(type.equals(“a”))
{
sa = new SortOne();
}
else if(type.equals(“b”))
{
sa = new SrotTwo();
}
else
{
……
}
sa.sort(inArray);
}
上面我們就完成了策略模式對該問題的解決,滿足了單一職責原則;有了一定的擴展性,如果新增一個算法的話,去實現SortAlgorithm接口就可以了。
當然,我們在客戶端代碼可以看到,策略模式依然沒有完全去掉客戶端對具體實現類的依賴,這使得我們增加一個新的算法以後,仍然需要對客戶端進行修改。所以策略模式和命令模式一樣,只是初步增加了一定的擴展性。如果要完全去掉客戶端對具體類的依賴,可以結合工廠模式來對該問題作進一步優化,具體做法大家可以自己做,這裏不再講述。
所謂策略模式,簡單說來就是對算法的封裝。在實際的編碼過程中,我們會遇到各種各種的選擇算法的問題,這時候就可以使用策略模式了,下面以一個例子來作爲本文的結束。
假設我們有一個POJO類:Employee,裏面存儲了很多用戶的信息,如下:
public class Employee
{
private int id;
private String name;
private double servedYears;
……
public void setId(int id)
{
this.id = id;
}
public int getId()
{
return this.id;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void setServedYears(double servedYears)
{
this.servedYears = servedYears;
}
public double getServedYears()
{
return this.servedYears;
}
……
}
現在我們有一個關於Employee的數組,需要對數組進行排序,我們知道該使用Arrays.sort()對該數組進行排序,但是在使用該方法的時候,我們首先要實現Comparator接口,如下:
Arrays.sort(emps,new Comparator(){
Public int compare(Object o1,Object o2)
{
return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
}
});
上面實現了一個對POJO對象的排序過程,現在的問題是我們分別需要對Employee類的id、name和servedYears爲索引進行排序。我們可以看出,這明顯是多個算法的問題,可以使用策略模式來解決問題:
以id爲索引的比較器:
public class IdComparator implements Comparator
{
Public int compare(Object o1,Object o2)
{
return ((Employee)o1).getId()-((Employee)o2).getId();
}
}
以name爲索引的比較器:
public class NameComparator implements Comparator
{
Public int compare(Object o1,Object o2)
{
return ((Employee)o1).getName()-((Employee)o2).getName();
}
}
以servedYears爲索引的比較器:
public class ServedYearsComparator implements Comparator
{
Public int compare(Object o1,Object o2)
{
return ((Employee)o1).getservedYears()-((Employee)o2).getServedYears();
}
}
然後,我們做一個工廠來獲取具體的比較器:
public class Factory
{
public static Comparator getInstance(String type)
{
if(type.equals(“id”))
{
return new IdComparator();
}
else if(type.equals(“name”))
{
return new NameComparator();
}
else
{
return ServedYearsComparator();
}
}
}
那麼我們就可以對客戶端進行編碼了:
public void sort(Employee[] emps,String type)
{
Arrays.sort(emps,Factory.getInstance(type));
}
我們可以看到,結合了工廠模式的策略模式的確使多算法的問題得到了很好的解決:抽取了各個算法來單獨關注,滿足了單一職責原則;滿足了依賴顛倒原則,有了很好的擴展性。等等。
還等什麼呢?趕快到實踐中去靈活運用這些模式吧!