策略模式(Strategy Pattern): 我的理解,將代碼中每個變化之處抽出,提煉成一個一個的介面或者抽象類,讓這些變化實現介面或繼承抽象類成為具體的變化類。再利用多態的功能,可將變化之處用介面或抽象類的對象代替,再用其子類為對象賦值,這樣就可以將對象隨時更換成具體的變化類。 枯燥的文字描述總 ...
策略模式(Strategy Pattern):
我的理解,將代碼中每個變化之處抽出,提煉成一個一個的介面或者抽象類,讓這些變化實現介面或繼承抽象類成為具體的變化類。再利用多態的功能,可將變化之處用介面或抽象類的對象代替,再用其子類為對象賦值,這樣就可以將對象隨時更換成具體的變化類。
枯燥的文字描述總是沒有實際的例子淺顯易懂。
舉例:(我是基於notepad++和cmd命令編譯運行的JAVA代碼)
現在有個鴨子俱樂部,裡面有各式各樣的鴨子(只有想不到,沒有做不到的鴨子)。
我們來一步一步實現這個鴨子類:
1.定義一個抽象鴨子類(Duck):
1 public abstract class Duck{//抽象鴨子類 2 public void fly(){//行為:飛行 3 System.out.println("I'm flying!"); 4 } 5 public void quack(){//行為:呱呱叫 6 System.out.println("Gua Gua!"); 7 } 8 public void swim(){//行為:游泳 9 System.out.println("I'm swimming!"); 10 } 11 }Duck
2.實現一個正常的鴨子類(GreenDuck):
1 public class GreenDuck extends Duck{//GreenDuck直接繼承Duck,什麼都不做 2 3 public GreenDuck(){ 4 } 5 }GreenDuck
3.測試類(DuckTest):
1 public class DuckTest{//測試類 2 public static void main(String args[]){ 3 GreenDuck greenDuck = new GreenDuck();//實例化一隻GreenDuck 4 greenDuck.fly(); 5 greenDuck.quack(); 6 greenDuck.swim(); 7 } 8 }DuckTest
編譯運行+結果:
大功告成!我們去慶祝一下。
正在慶祝,鴨子俱樂部來電話說,我們不僅只要一隻GreenDuck,還要RedDuck...不管什麼顏色的鴨子都要。你一口應承下來,沒問題只是多寫幾個繼承的類而已。
鴨子俱樂部繼續說道,我們還要不會飛,不會叫的橡皮鴨,所有顏色的橡皮鴨來一套。你很心虛的答應了,可能今晚要加班加點了,讓各種顏色的橡皮鴨繼承Duck但是要覆蓋其中的fly和quack行為。
鴨子俱樂部沒完沒了的繼續說道,我們還要各種顏色的鴨鳴器,它們只會叫不會飛。現在你心裡一定恨死各種顏色這個單詞了。
鴨子俱樂部嘴停不下來說各種顏色的...
.......
第二天,哭暈在廁所中。
現在,來看看到底是什麼問題導致我們要不停的重覆寫大量的代碼:各種顏色
沒錯就是這個單詞讓我們不停地去寫各種各樣的鴨子實現類(都繼承自Duck抽象類),並且有的鴨子不會飛,有的會飛不會叫...
現在有沒有感覺到繼承帶來的恐懼感?我們可以讓所有的同類鴨子都繼承自Duck抽象類,但是每種鴨子都有自己獨特的行為,導致我們要不停地去覆蓋Duck抽象類中的行為。
問題找到了。就是繼承自抽象類的行為不符合每種鴨子獨特的行為導致我們不停地去手動改寫或添加行為。我們寫這麼多的重覆代碼,沒有將代碼復用,比如,有的鴨子會飛,有的鴨子會叫,有的鴨子會游泳,有的鴨子不會叫...這麼多的行為都寫在鴨子實現類中,導致代碼冗餘,沒有將它們復用。
下麵讓我們的救星:策略模式(Strategy Pattern)登場:
1.首先,fly()和quack()兩個方法是一直在變化的,所以我們將這兩個變化之處從Duck抽象類中提煉出來變成FlyBehavior介面和QuackBehavior介面,併在Duck抽象類中定義flyBehavior和quackBehavior兩個對象。
1 public abstract class Duck{//抽象鴨子類 2 3 /*增加兩個介面對象*/ 4 FlyBehavior flyBehavior;//飛行類對象 5 QuackBehavior quackBehavior;//呱呱叫類對象 6 7 public Duck(){ 8 } 9 10 //去除下麵兩個方法 11 /*public void fly(){//行為:飛行 12 System.out.println("I'm flying!"); 13 } 14 public void quack(){//行為:呱呱叫 15 System.out.println("Gua Gua!"); 16 }*/ 17 18 /*增加下麵兩個方法,這就是將Duck類的行為委托給兩個介面對象實現*/ 19 public void performFly(){//將fly()委托給flyBehavior對象實現 20 flyBehavior.fly(); 21 } 22 public void performQuack(){//將quack()委托給quackBehavior對象實現 23 quackBehavior.quack(); 24 } 25 26 27 public void swim(){//行為:游泳 28 System.out.println("I'm swimming!"); 29 } 30 }Duck
1 public interface FlyBehavior{//從Duck抽象類中抽出的fly()方法變成了FlyBehavior介面 2 public void fly(); 3 }FlyBehavior
1 public interface QuackBehavior{//從Duck抽象類中抽出的quack()方法變成了QuackBehavior介面 2 public void quack(); 3 }QuackBehavior
其次,將變化具體類分別繼承FlyBehavior和QuackBehavior兩個介面:
兩個飛行具體變化類:
1 public class FlyWithWings implements FlyBehavior{ 2 public void fly(){ 3 System.out.println("I'm flying!"); 4 } 5 }FlyWithWings
1 public class FlyNoWay implements FlyBehavior{ 2 public void fly(){ 3 System.out.println("I can't fly!"); 4 } 5 }FlyNoWay
兩個呱呱叫具體變化類:
1 public class Quack implements QuackBehavior{ 2 public void quack(){ 3 System.out.println("Quack quack!"); 4 } 5 }Quack
1 public class MuteQuack implements QuackBehavior{ 2 public void quack(){ 3 System.out.println("<< Silence >>"); 4 } 5 }MuteQuack
最後,實現一個具體類和測試類:
1 public class GreenDuck extends Duck{//GreenDuck直接繼承Duck 2 3 public GreenDuck(){ 4 flyBehavior = new FlyWithWings(); 5 quackBehavior = new Quack(); 6 } 7 8 /*增加一個展示自己是什麼鴨子的方法*/ 9 public void display(){ 10 System.out.println("I'm GreenDuck!"); 11 } 12 }GreenDuck
1 public class DuckTest{//測試類 2 public static void main(String args[]){ 3 GreenDuck greenDuck = new GreenDuck();//實例化一隻GreenDuck 4 greenDuck.performFly(); 5 greenDuck.performQuack(); 6 greenDuck.swim(); 7 greenDuck.display(); 8 } 9 }DuckTest
編譯運行,結果:
上面的結果,我們可以隨時隨地的實現不同的具體的鴨子類了,只要在具體的鴨子類中為flyBehavior和quackBehavior實現不同的變化類就好。
2.動態的實現具體變化類的改變:
在Duck類中添加兩個新方法(setFlyBehavior(Flybehavior fb)和 setQuackBehavior(QuackBehavior qb) ):
1 public abstract class Duck{//抽象鴨子類 2 3 /*增加兩個介面對象*/ 4 FlyBehavior flyBehavior;//飛行類對象 5 QuackBehavior quackBehavior;//呱呱叫類對象 6 7 public Duck(){ 8 } 9 10 //去除下麵兩個方法 11 /*public void fly(){//行為:飛行 12 System.out.println("I'm flying!"); 13 } 14 public void quack(){//行為:呱呱叫 15 System.out.println("Gua Gua!"); 16 }*/ 17 18 /*增加下麵兩個方法,這就是將Duck類的行為委托給兩個介面對象實現*/ 19 public void performFly(){//將fly()委托給flyBehavior對象實現 20 flyBehavior.fly(); 21 } 22 public void performQuack(){//將quack()委托給quackBehavior對象實現 23 quackBehavior.quack(); 24 } 25 26 /*添加兩個新方法,可以動態的改變具體變化類*/ 27 public void setFlyBehavior(FlyBehavior fb){ 28 flyBehavior = fb; 29 } 30 public void setQuackBehavior(QuackBehavior qb){ 31 quackBehaior = qb; 32 } 33 34 35 public void swim(){//行為:游泳 36 System.out.println("I'm swimming!"); 37 } 38 }Duck
改造測試類:
1 public class DuckTest{//測試類 2 public static void main(String args[]){ 3 GreenDuck greenDuck = new GreenDuck();//實例化一隻GreenDuck 4 greenDuck.performFly();//一開始GreenDuck會飛 5 greenDuck.performQuack();//一開始GreenDuck會叫 6 7 /*動態改變greenDuck的行為*/ 8 greenDuck.setFlyBehavior(new FlyNoWay()); 9 greenDuck.setQuackBehavior(new MuteQuack()); 10 11 greenDuck.performFly();//現在不會飛了 12 greenDuck.performQuack();//現在不會叫了 13 } 14 }DuckTest
編譯運行,結果:
代碼下載網址:
https://github.com/lanshanxiao/Head-First-Design-Pattern/tree/master/1.%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F(StrategyPattern)%E8%AE%B2%E8%A7%A3%E4%BB%A3%E7%A0%81/%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0
提煉一下思想:
1.封裝
2.“有一個” 比 “是一個” 好(has-a 比 is-a好)
3.多用組合少用繼承
4.封裝變化
5.正對介面編程,不針對實現編程